public void Run()
{
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("NetMQPoller is already running");
#if NET35
m_pollerThread = Thread.CurrentThread;
#else
var oldSynchronisationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new NetMQSynchronizationContext(this));
m_isSchedulerThread.Value = true;
#endif
m_stopSignaler.Reset();
m_switch.SwitchOn();
try
{
// the sockets may have been created in another thread, to make sure we can fully use them we do full memory barrier
// at the beginning of the loop
Thread.MemoryBarrier();
// Recalculate all timers now
foreach (var timer in m_timers)
{
if (timer.Enable)
timer.When = Clock.NowMs() + timer.Interval;
}
// Run until stop is requested
while (!m_stopSignaler.IsStopRequested)
{
if (m_isPollSetDirty)
RebuildPollset();
var pollStart = Clock.NowMs();
// Set tickless to "infinity"
long tickless = pollStart + int.MaxValue;
// Find the When-value of the earliest timer..
foreach (var timer in m_timers)
{
// If it is enabled but no When is set yet,
if (timer.When == -1 && timer.Enable)
{
// Set this timer's When to now plus it's Interval.
timer.When = pollStart + timer.Interval;
}
// If it has a When and that is earlier than the earliest found thus far,
if (timer.When != -1 && tickless > timer.When)
{
// save that value.
tickless = timer.When;
}
}
// Compute a timeout value - how many milliseconds from now that that earliest-timer will expire.
var timeout = tickless - pollStart;
// Use zero to indicate it has already expired.
if (timeout < 0)
timeout = 0;
var isItemAvailable = false;
if (m_pollSet.Length != 0)
{
isItemAvailable = m_selector.Select(m_pollSet, m_pollSet.Length, timeout);
}
else if (timeout > 0)
{
//TODO: Do we really want to simply sleep and return, doing nothing during this interval?
//TODO: Should a large value be passed it will sleep for a month literally.
// Solution should be different, but sleep is more natural here than in selector (timers are not selector concern).
Debug.Assert(timeout <= int.MaxValue);
Thread.Sleep((int)timeout);
}
// Get the expected end time in case we time out. This looks redundant but, unfortunately,
// it happens that Poll takes slightly less than the requested time and 'Clock.NowMs() >= timer.When'
// may not true, even if it is supposed to be. In other words, even when Poll times out, it happens
// that 'Clock.NowMs() < pollStart + timeout'
var expectedPollEnd = !isItemAvailable ? pollStart + timeout : -1L;
// that way we make sure we can continue the loop if new timers are added.
// timers cannot be removed
foreach (var timer in m_timers)
{
if ((Clock.NowMs() >= timer.When || expectedPollEnd >= timer.When) && timer.When != -1)
{
timer.InvokeElapsed(this);
if (timer.Enable)
timer.When = Clock.NowMs() + timer.Interval;
}
}
for (var i = 0; i < m_pollSet.Length; i++)
{
SelectItem item = m_pollSet[i];
if (item.Socket != null)
{
NetMQSocket socket = m_pollact[i];
if (item.ResultEvent.HasError())
{
if (++socket.Errors > 1)
{
Remove(socket);
item.ResultEvent = PollEvents.None;
}
}
else
{
socket.Errors = 0;
}
if (item.ResultEvent != PollEvents.None)
socket.InvokeEvents(this, item.ResultEvent);
}
else if (item.ResultEvent.HasError() || item.ResultEvent.HasIn())
{
Action<Socket> action;
if (m_pollinSockets.TryGetValue(item.FileDescriptor, out action))
action(item.FileDescriptor);
}
}
}
}
finally
{
try
{
foreach (var socket in m_sockets.ToList())
Remove(socket);
}
finally
{
#if NET35
m_pollerThread = null;
#else
m_isSchedulerThread.Value = false;
SynchronizationContext.SetSynchronizationContext(oldSynchronisationContext);
#endif
m_switch.SwitchOff();
}
}
}