/// <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); } } }
/// <summary> /// Poll as long as the given Func evaluates to true. /// </summary> /// <param name="condition">a Func that returns a boolean value, to evaluate on each poll-iteration to decide when to exit the loop</param> /// <exception cref="ObjectDisposedException">This poller must not have already been disposed.</exception> /// <exception cref="InvalidOperationException">This poller must not have already been started.</exception> private void PollWhile([NotNull, InstantHandle] Func <bool> condition) { if (m_disposed) { throw new ObjectDisposedException("Poller is disposed"); } if (m_isStarted) { throw new InvalidOperationException("Poller is started"); } if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = "NetMQPollerThread"; } m_isStoppedEvent.Reset(); m_isStarted = true; 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_isStarted = false; m_isStoppedEvent.Set(); } } }
/// <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> 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(); } } }
private void PollWhile(Func <bool> condition) { if (m_disposed) { throw new ObjectDisposedException("Poller is disposed"); } if (m_isStarted) { throw new InvalidOperationException("Poller is started"); } if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = "NetMQPollerThread"; } m_isStoppedEvent.Reset(); m_isStarted = true; try { // the sockets may have been created in another thread, to make sure we can fully use them we do full memory barried // at the begining of the loop Thread.MemoryBarrier(); // Recalculate all timers now foreach (NetMQTimer netMQTimer in m_timers) { if (netMQTimer.Enable) { netMQTimer.When = Clock.NowMs() + netMQTimer.Interval; } } while (condition()) { if (m_isDirty) { RebuildPollset(); } var pollStart = Clock.NowMs(); var timeout = TicklessTimer(); var nbEvents = ZMQ.Poll(m_pollset, m_pollSize, 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 = nbEvents == 0 ? 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++) { PollItem item = m_pollset[itemNbr]; if (item.Socket != null) { NetMQSocket socket = m_pollact[itemNbr] as NetMQSocket; if (item.ResultEvent.HasFlag(PollEvents.PollError) && !socket.IgnoreErrors) { 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.HasFlag(PollEvents.PollError) || item.ResultEvent.HasFlag(PollEvents.PollIn)) { Action <Socket> action; if (m_pollinSockets.TryGetValue(item.FileDescriptor, out action)) { action(item.FileDescriptor); } } } } if (m_zombies.Any()) { // Now handle any timer zombies // This is going to be slow if we have many zombies foreach (NetMQTimer netMQTimer in m_zombies) { m_timers.Remove(netMQTimer); } m_zombies.Clear(); } } } finally { m_isStoppedEvent.Set(); foreach (var socket in m_sockets.ToList()) { RemoveSocket(socket); } } }
public void Start() { Thread.CurrentThread.Name = "NetMQPollerThread"; m_isStoppedEvent.Reset(); m_isStarted = true; try { // the sockets may have been created in another thread, to make sure we can fully use them we do full memory barried // at the begining of the loop Thread.MemoryBarrier(); // Recalculate all timers now foreach (NetMQTimer netMQTimer in m_timers) { if (netMQTimer.Enable) { netMQTimer.When = Clock.NowMs() + netMQTimer.Interval; } } while (!m_cancellationTokenSource.IsCancellationRequested) { if (m_isDirty) { RebuildPollset(); } ZMQ.Poll(m_pollset, m_pollSize, TicklessTimer()); // 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 && timer.When != -1) { timer.InvokeElapsed(this); if (timer.Enable) { timer.When = timer.Interval + Clock.NowMs(); } } } for (int itemNbr = 0; itemNbr < m_pollSize; itemNbr++) { NetMQSocket socket = m_pollact[itemNbr]; PollItem item = m_pollset[itemNbr]; if (item.ResultEvent.HasFlag(PollEvents.PollError) && !socket.IgnoreErrors) { 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); } } if (m_zombies.Any()) { // Now handle any timer zombies // This is going to be slow if we have many zombies foreach (NetMQTimer netMQTimer in m_zombies) { m_timers.Remove(netMQTimer); } m_zombies.Clear(); } } } finally { m_isStoppedEvent.Set(); } }