/// <summary> /// Ensure that messages in the low priority sending queue are sent in a timely manner, but in a way such that they do not interfere with /// high priority messages. /// </summary> private void SendThread() { while (!m_Disposed) { //Thread.Sleep(25); //Uncomment to simulate a really slow network. using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) { //find out if it is currently safe to send if (m_PendingOutboundMessageCount < m_MaxConcurrentSends) { SendParameters nextSendItem = null; using (Synchronizer.Lock(m_SendingParticipants)) { nextSendItem = (SendParameters)m_SendQueue.Dequeue(m_SendingParticipants, m_CurrentSlideID); } if (nextSendItem != null) { //Trace.WriteLine("SendNow: sequence=" + nextSendItem.MessageSequence.ToString()); //Trace.WriteLine("SendNow: CurrentSlide=" + m_CurrentSlideID.ToString() + // ";msg slide guid=" + nextSendItem.Tags.SlideID.ToString() + // ";IsPublicNode=" + nextSendItem.IsPublicNode.ToString(), ""); SendNow(nextSendItem); continue; } } // If nothing can be sent, wait for the queue's status to change. // This happens either when new items are inserted or m_PendingOutboundMessageCount is updated. cookie.Wait(); } } }
protected void FlusherThread() { using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_Lock)) { for (int refresh = 0;;) { try { if (this.m_Disposed) { return; } // Wait a random amount of time (a different period each time). cookie.Wait(this.m_Random.Next(this.m_MaxRandomWait)); if (this.m_Disposed) { return; } foreach (Sets sets in this.m_Sets.Values) { // Send an RtpNackMessage containing the current nack set (if not empty). if (sets.Current != null) { this.m_Sender.SendNack(new RtpNackMessage(sets.SSRC, sets.Current)); sets.Flush(); } } // After a certain amount of time, copy the entire reference set // to the nack set. However, do this *after* flushing, so we wait // a little while to see if other clients will send the same NACK. if (++refresh > REFRESH_SCALE) { refresh = 0; foreach (Sets sets in this.m_Sets.Values) { sets.Refresh(); } } } catch (Exception e) { // Report the error, but do not break out of the thread. // TODO: Log the error to the system event log. Trace.Fail("RTPNackManager encountered an error: " + e.ToString(), e.StackTrace); } } } }
/// <summary> /// The thread loop which executes queued events. /// </summary> /// <remarks> /// The loop iterates each time <c>Monitor.Pulse(this.m_Queue)</c> is called. /// It is expected that this will <em>only</em> happen when either <see cref="Invoker"/>s /// are added to the queue <em>or</em> <see cref="Dispose"/> is called. /// </remarks> private void ProcessMessages() { Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_Queue); try { while (!this.m_Disposed) { try { // Wait until we have a message in the queue. if (this.m_Queue.Count <= 0) { cookie.Wait(); } // Halt the thread as soon as the ThreadEventQueue is disposed. if (this.m_Disposed) { return; } // Monitor.Pulse is only called either when the ThreadEventQueue is disposed // or when a message is added to the queue. Debug.Assert(this.m_Queue.Count > 0); Invoker invoker = this.m_Queue.Dequeue(); // Temporarily release the lock so other threads can add things to the // queue while the message is processing. This also avoids potential deadlocks. cookie.Dispose(); try { invoker.Method(); } finally { cookie = Synchronizer.Lock(this.m_Queue); } } catch (Exception e) { // Report the error, but do not break out of the thread. // TODO: Log the error to the system event log. Trace.Fail("The ThreadEventQueue encountered an error: " + e.ToString(), e.StackTrace); } } } finally { cookie.Dispose(); } }
private void BeaconSenderThreadStart() { while (true) { try { using (Synchronizer.Cookie cookie = Synchronizer.Lock(this)) { if (this.m_Disposed) { return; } // Get the time remaining before sending the next beacon. TimeSpan interval = this.BeaconInterval; long remaining = interval.Ticks; // Get the current time so we can see how much time we've actually waited. long start = DateTime.Now.Ticks; do { // Wait until either the time expired, or until Monitor.Pulse(this) // indicates that the BeaconService's state has changed. cookie.Wait(new TimeSpan(remaining)); // Return immediately if we've been disposed. if (this.m_Disposed) { return; } // If the interval was previously infinite, // restart the timer from scratch with the new interval. if (remaining < 0) { remaining = this.BeaconInterval.Ticks; continue; } // Subtract the actual time elapsed while waiting // (this will be less than "remaining" if we were Pulsed). remaining -= (DateTime.Now.Ticks - start); // In case the interval has changed, get the new interval. TimeSpan replaced = this.BeaconInterval; if (replaced.Ticks < 0) { // Special case if the new interval is infinite; // otherwise the math is wrong since -1 milliseconds is a special value. remaining = replaced.Ticks; continue; } else { // Add or subtract the difference between the new and old intervals. // If the new interval is longer, this increases the time remaining. // if the new interval is shorter, this decreases the time remaining. remaining += (replaced.Ticks - interval.Ticks); interval = replaced; } // As long as we've got more time before the next beacon, repeat. } while(remaining > 0); Message message = this.MakeBeaconMessage(); if (message != null) { // Ideally, the beacon message wouldn't be subject to rebroadcast if dropped. // It's periodic, so subsequent beacons will replace any dropped frames. // But we want beacon messages to trigger rebroadcast of *other* dropped frames. // Also, the beacon messages should be processed in the same order as other // messages dealing with the state of the presentation, to avoid timing issues. // Therefore, beacon messages are sent with Default priority instead of RealTime. this.m_Sender.Send(message, MessagePriority.Default); } } } catch (Exception e) { Trace.WriteLine("BeaconService encountered an error: " + e.ToString() + "\r\n" + e.StackTrace); } } }