private void PollWhile([NotNull, InstantHandle] Func<bool> condition)
{
if (m_disposed)
{
throw new ObjectDisposedException("Poller is disposed");
}
if (m_switch.Status)
{
throw new InvalidOperationException("Poller is started");
}
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "NetMQPollerThread";
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;
}
}
// Do this until the given Func evaluates to false...
while (condition())
{
if (m_isDirty)
{
RebuildPollset();
}
var pollStart = Clock.NowMs();
var timeout = TicklessTimer();
var isItemsAvailable = false;
if (m_pollSize > 0)
{
isItemsAvailable = m_selector.Select(m_pollset, m_pollSize, timeout);
}
else
{
// Don't pass anything less than 0 to sleep or risk an out of range exception or worse - infinity. Do not sleep on 0 from orginal code.
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 literaly.
// Solution should be different, but sleep is more natural here than in selector (timers are not selector concern).
Thread.Sleep(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 = !isItemsAvailable ? pollStart + timeout : -1;
// that way we make sure we can continue the loop if new timers are added.
// timers cannot be removed
int timersCount = m_timers.Count;
for (int i = 0; i < timersCount; i++)
{
var timer = m_timers[i];
if ((Clock.NowMs() >= timer.When || expectedPollEnd >= timer.When) && timer.When != -1)
{
timer.InvokeElapsed(this);
if (timer.Enable)
{
timer.When = timer.Interval + Clock.NowMs();
}
}
}
for (int itemNbr = 0; itemNbr < m_pollSize; itemNbr++)
{
SelectItem item = m_pollset[itemNbr];
if (item.Socket != null)
{
NetMQSocket socket = m_pollact[itemNbr];
if (item.ResultEvent.HasError())
{
socket.Errors++;
if (socket.Errors > 1)
{
RemoveSocket(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);
}
}
}
}
if (m_zombies.Count > 0)
{
// Now handle any timer zombies
// This is going to be slow if we have many zombies
foreach (var netMQTimer in m_zombies)
{
m_timers.Remove(netMQTimer);
}
m_zombies.Clear();
}
}
}
finally
{
try
{
foreach (var socket in m_sockets.ToList())
{
RemoveSocket(socket);
}
}
finally
{
m_switch.SwitchOff();
}
}
}