Пример #1
0
        /// <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();
                }
            }
        }
Пример #2
0
        private void SendCallback(IAsyncResult ar)
        {
            SendState state = (SendState)ar.AsyncState;

            try {
                int len = state.SendSocket.EndSend(ar);
                if (len != state.SendParameters.Length)
                {
                    //I don't know how this can happen, but just in case..
                    Debug.Assert(false, "Critical error: Server did not send the expected number of bytes.  Buffer length = " + state.SendParameters.Length.ToString() +
                                 "; bytes sent = " + len.ToString() + "; remote endpoint = " + state.SendSocket.RemoteEndPoint.ToString());
                }
            }
            catch (SocketException se) {
                Trace.WriteLine("SendCallback SocketException errorcode: " + se.ErrorCode.ToString(), this.GetType().ToString());
                this.DisableClient(state.SendParameters.Id);
            }
            catch (ObjectDisposedException ode) {
                Trace.WriteLine("SendCallback: " + ode.Message, this.GetType().ToString());
                this.DisableClient(state.SendParameters.Id);
            }
            catch (Exception e) {
                Trace.WriteLine(e.ToString(), this.GetType().ToString());
                this.DisableClient(state.SendParameters.Id);
            }
            finally {
                using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_SendQueue)) {
                    using (Synchronizer.Lock(m_SendingParticipants)) {
                        if (m_SendingParticipants.ContainsKey(state.SendParameters.Id))
                        {
                            ((ReferenceCounter)m_SendingParticipants[state.SendParameters.Id]).Decrement();
                        }
                        else
                        {
                            Debug.Assert(false, "Server send callback found a missing reference counter.");
                        }
                        --m_PendingOutboundMessageCount;
                        Debug.Assert(m_PendingOutboundMessageCount >= 0, "Server send callback found a negative pending message count.");

                        //Notify threads closing the socket if this is the last item in the socket queue.
                        if (m_ClosingSockets.ContainsKey(state.SendSocket))
                        {
                            if (((ReferenceCounter)m_SendingParticipants[state.SendParameters.Id]).IsZero)
                            {
                                ((ManualResetEvent)m_ClosingSockets[state.SendSocket]).Set();
                            }
                        }

                        // Notify the sender thread that the queue's status has changed.
                        cookie.PulseAll();
                    }
                }
            }
        }
Пример #3
0
 /// <summary>
 /// If a client reconnects, update the socket map and enable the client's queue
 /// </summary>
 /// <param name="id"></param>
 /// <param name="s"></param>
 internal void Reconnect(ClientData client, ulong lastMessage, ulong lastChunk)
 {
     using (Synchronizer.Lock(this.m_SocketMap)) {
         if (m_SocketMap.ContainsKey(client.Id))
         {
             m_SocketMap[client.Id] = client.Socket;
         }
     }
     using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_SendQueue)) {
         m_SendQueue.EnableClient(client, lastMessage, lastChunk);
         cookie.PulseAll();
     }
 }
Пример #4
0
        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);
                    }
                }
            }
        }
Пример #5
0
        protected virtual void Dispose(bool disposing)
        {
            this.m_Disposed = true;
            if (disposing)
            {
                m_Instructor.Changed["CurrentDeckTraversal"].Remove(new PropertyEventHandler(OnCurrentDeckTraversalChanged));
                if (m_DeckTraversal != null)
                {
                    m_DeckTraversal.Changed["Current"].Remove(new PropertyEventHandler(OnTableOfContentsEntryChanged));
                }

                // Notify the sending thread that we're disposed.
                using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_SendQueue))
                    cookie.PulseAll();
            }
        }
Пример #6
0
        /// <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();
            }
        }
Пример #7
0
        /// <summary>
        /// Enqueue the send to occur when network bandwidth is available.
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        /// <param name="socket"></param>
        /// <param name="participant"></param>
        internal void Send(byte[] buffer, int length, ClientData client, MessageTags tags,
                           ulong messageSequence, ulong chunkSequence, bool isHeartbeat)
        {
            using (Synchronizer.Lock(m_SocketMap)) {
                Debug.Assert(m_SocketMap.ContainsKey(client.Id), "TCPServerSender.Send found a missing Socket Map entry.");
            }

            //Trace.WriteLine("TCPServerSender.Send seq=" + messageSequence.ToString());

            //Enqueue the message
            using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) {
                m_SendQueue.Enqueue(new SendParameters(buffer, length, client.Id, tags, messageSequence, chunkSequence, isHeartbeat),
                                    client.Participant);

                // Notify the sender thread that the queue's status has changed.
                cookie.PulseAll();
            }
        }
Пример #8
0
        protected virtual void Dispose(bool disposing)
        {
            this.m_Disposed = true;
            if (disposing)
            {
                using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_Lock)) {
                    if (this.m_Disposed)
                    {
                        return;
                    }
                    this.m_Disposed = true;

                    this.m_Classroom.Changed["Participants"].Remove(new PropertyEventHandler(this.HandleClassroomParticipantsChanged));

                    // Setting m_Disposed to true will cause the thread to stop, once we pulse the monitor.
                    Monitor.Pulse(this.m_Lock);
                }
            }
        }
Пример #9
0
        /// <summary>
        /// Stops executing queued events, stops the event thread, and releases any
        /// resources used by the <see cref="ThreadEventQueue"/>.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            try {
                if (disposing)
                {
                    using (Synchronizer.Cookie cookie = Synchronizer.Lock(this.m_Queue)) {
                        if (this.m_Disposed)
                        {
                            return;
                        }
                        this.m_Disposed = true;

                        // Setting m_Disposed to true will cause the ProcessMessages thread to stop, once we pulse the monitor.
                        Monitor.Pulse(this.m_Queue);
                    }
                }
            } finally {
                base.Dispose(disposing);
            }
        }
Пример #10
0
        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);
                }
            }
        }