private void RebuildPollset() { #if !NET35 Debug.Assert(m_isSchedulerThread.Value); #endif // Recreate the m_pollSet and m_pollact arrays. m_pollSet = new NetMQSelector.Item[m_sockets.Count + m_pollinSockets.Count]; m_pollact = new NetMQSocket[m_sockets.Count]; // For each socket in m_sockets, // put a corresponding SelectItem into the m_pollSet array and a reference to the socket itself into the m_pollact array. uint index = 0; foreach (var socket in m_sockets) { m_pollSet[index] = new NetMQSelector.Item(socket, socket.GetPollEvents()); m_pollact[index] = socket; index++; } foreach (var socket in m_pollinSockets.Keys) { m_pollSet[index] = new NetMQSelector.Item(socket, PollEvents.PollError | PollEvents.PollIn); index++; } // Mark this as NOT having any fresh events to attend to, as yet. m_isPollSetDirty = false; }
/// <summary> /// Runs the poller on the caller's thread. Only returns when <see cref="Stop"/> or <see cref="StopAsync"/> are called from another thread. /// </summary> private void RunPoller() { try { // 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 the 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_netMqSelector.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++) { NetMQSelector.Item 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()) { if (m_pollinSockets.TryGetValue(item.FileDescriptor, out Action <Socket> action)) { action(item.FileDescriptor); } } } } #if !NET35 // Try to dequeue and execute all pending tasks before stopping poller while (m_tasksQueue.TryDequeue(out Task task, TimeSpan.Zero)) { TryExecuteTask(task); } #endif } finally { foreach (var socket in m_sockets.ToList()) { Remove(socket); } } }