Provides methods and properties for tracking messages, errors, processing time, deadlocks and other issues that may occur in the state server
Example #1
0
        /// <summary>
        /// Sends data via the socket asynchronously
        /// </summary>
        /// <param name="Message">Data to transmit</param>
        public void Send(ResponseData Message)
        {
            IBuffer sendBuffer = null;

            //TODO: ENHANCEMENT: Log consecutive bad request response types and use that information to disconnect socket after 3
            try
            {
                lock (syncSocket)
                {
                    if (sentMsgs.Count > 1)
                    {
                        sentMsgs.Dequeue();
                    }
                    sentMsgs.Enqueue(Message);

                    //Log error if .Data is null -- this will help raise a flag if the message is being resent after .ClearData was called
                    Diags.Assert(Message.Data != null, "ASSERTION FAILED: Message Data is null", new System.Diagnostics.StackTrace().ToString());

                    sendBuffer = bufferPool.GetBuffer(Message.Data.LongLength);
                    sendBuffer.CopyFrom(Message.Data);
                    socket.BeginSend(sendBuffer.GetSegments(), SocketFlags.None, CompleteSend, sendBuffer);

                    Message.ClearData(); //free some memory
                }
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
                if (sendBuffer != null)
                {
                    sendBuffer.Dispose();
                    sendBuffer = null;
                }
            }
        }
Example #2
0
        /// <summary>
        /// Completes an asynchronous send
        /// </summary>
        /// <param name="ar">AsyncResul obtained from BeginSend</param>
        private void CompleteSend(IAsyncResult ar)
        {
            // Complete asynchronous send
            IBuffer sendBuffer = (IBuffer)ar.AsyncState;

            try
            {
                if (!socket.Connected)
                {
                    return;
                }
                lock (syncSocket)
                {
                    socket.EndSend(ar);
                }
                sendBuffer.Dispose();
                sendBuffer = null;
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
                if (sendBuffer != null)
                {
                    sendBuffer.Dispose();
                    sendBuffer = null;
                }
            }
        }
Example #3
0
        /// <summary>
        /// Closes the socket gracefully
        /// </summary>
        public void Close()
        {
            //TODO: ENHANCEMENT: Seems like the graceful shutdown process in this method is not working well
            //Is it because of the 1 ms timeout?
            //I see a lot more stale Peer connections than stale web server connections, so it looks like
            //Thw web server connections are closing better than the peer connections. Investigate this.

            try
            {
                lock (syncSocket)
                {
                    if (socket.Connected)
                    {
                        socket.Shutdown(SocketShutdown.Both);
                    }
                    isClosing = true;
                    socket.Close(1);
                }

                if (recvBuffer != null)
                {
                    recvBuffer.Dispose();
                }
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
            }
        }
Example #4
0
        /// <summary>
        /// Begins an asynchronous receive
        /// </summary>
        /// <param name="Buffer">Buffer to store received data</param>
        /// <param name="ReadCallBack">Method to call on receiving data</param>
        /// <param name="StateObject">State object to be passed to ReadCallBack</param>
        /// <returns>AsyncResult for the asynchronous operation</returns>
        public IAsyncResult BeginReceive(int BufferLength, AsyncCallback ReadCallBack, object StateObject)
        {
            try
            {
                if (recvBuffer == null || recvBuffer.IsDisposed)
                {
                    recvBuffer = bufferPool.GetBuffer(BufferLength);
                }
                else if (recvBuffer.Size < BufferLength)
                {
                    recvBuffer.Dispose();
                    recvBuffer = bufferPool.GetBuffer(BufferLength);
                }

                lock (syncSocket)
                {
                    return(socket.BeginReceive(recvBuffer.GetSegments(), SocketFlags.None, ReadCallBack, StateObject));
                }
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
                return(null);
            }
        }
Example #5
0
        /// <summary>
        /// Ends an asynchronous Receive
        /// </summary>
        /// <param name="ar">AsyncResult obtained from BeginReive</param>
        /// <param name="Error">Indicates an error occured while receiving data</param>
        /// <returns>Received Data</returns>
        public byte[] EndReceive(IAsyncResult ar, out bool Error)
        {
            Error = false;
            int bytesRead = 0;

            try
            {
                lock (syncSocket)
                {
                    bytesRead = socket.EndReceive(ar);
                }
            }
            catch (ObjectDisposedException ex)
            {
                if (!isClosing)
                {
                    Diags.LogSocketException(ex);
                }
                Error = true;
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
                Error = true;
            }

            byte[] readData;
            if (Error || bytesRead < 0)
            {
                readData = new byte[0];
            }
            else
            {
                readData = new byte[bytesRead];
            }


            if (recvBuffer != null && !recvBuffer.IsDisposed)
            {
                if (!Error && bytesRead > 0)
                {
                    recvBuffer.CopyTo(readData, 0, bytesRead);
                }

                //Dispose buffer if it's greater than a specified threshold
                if (recvBuffer.Size > BufferRenewalSizeThreshold)
                {
                    recvBuffer.Dispose();
                }
            }

            return(readData);
        }
Example #6
0
 /// <summary>
 /// Ends an asynchronous Connect
 /// </summary>
 /// <param name="ar">AsyncResult obtained from BeginConnect</param>
 public void EndConnect(IAsyncResult ar)
 {
     try
     {
         lock (syncSocket)
         {
             isOutbound = true;
             socket.EndConnect(ar);
         }
     }
     catch (Exception ex)
     {
         Diags.LogSocketException(ex);
     }
 }
Example #7
0
 /// <summary>
 /// Begins an asynchronous Connect
 /// </summary>
 /// <param name="Host">Host to connect to</param>
 /// <param name="Port">Port number to connect to</param>
 /// <param name="ConnectCallBack">Callback to call on connecting</param>
 /// <param name="StateObject">State object to pass to ConnectCallback</param>
 /// <returns>AsyncResult for the asynchronous operation</returns>
 public IAsyncResult BeginConnect(string Host, int Port, AsyncCallback ConnectCallBack, object StateObject)
 {
     try
     {
         lock (syncSocket)
         {
             return(socket.BeginConnect(Host, Port, ConnectCallBack, StateObject));
         }
     }
     catch (Exception ex)
     {
         Diags.LogSocketException(ex);
         return(null);
     }
 }
Example #8
0
        /// <summary>
        /// Listens on a specified port on the machine
        /// </summary>
        /// <param name="Port">Port number</param>
        /// <param name="AcceptCallback">Callback for accepting new connections</param>
        /// <returns>.NET Socket if successful, Null if not</returns>
        public static Socket Listen(int Port, AsyncCallback AcceptCallback)
        {
            Socket listener;
            // Start Listening on Web Server Socket
            IPEndPoint wsEndPoint = new IPEndPoint(IPAddress.Any, Port);

            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(wsEndPoint);
                listener.Listen(MaxConnections);
                listener.BeginAccept(AcceptCallback, listener);
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
                return(null);
            }


            return(listener);
        }
Example #9
0
        /// <summary>
        /// Terminates a connection
        /// </summary>
        public void Abort()
        {
            try
            {
                lock (syncSocket)
                {
                    if (socket.Connected)
                    {
                        socket.Shutdown(SocketShutdown.Send);
                    }
                    isClosing = true;
                    socket.Close();
                }

                if (recvBuffer != null)
                {
                    recvBuffer.Dispose();
                }
            }
            catch (Exception ex)
            {
                Diags.LogSocketException(ex);
            }
        }
Example #10
0
        /// <summary>
        /// Updates or inserts a session in the dictionary
        /// </summary>
        /// <param name="Key">Session Key</param>
        /// <param name="Session">Session object</param>
        /// <param name="InsertOnly">Indicates that session should only be inserted if it does not already exist</param>
        /// <param name="UpdateIfNotFound">Indicates whether session should be updated if the session was not found. If set to flase, this gives the caller a chance to query the network before trying again </param>
        /// <param name="LockedSessionInfo">Locked session information if session is locked</param>
        /// <returns>Result of Action</returns>
        private SessionActionResult UpSert(string Key, ISessionObject Session, bool InsertOnly, bool UpdateIfNotFound, out SessionResponseInfo LockedSessionInfo)
        {
            // Look for the session using a reader lock.
            // If session is not found, switch to a writer lock and insert item.
            // If session is found:
            // Perform an atomic compare exchange on the variable 'InUse'
            // If session is in Use, try Upsert again from the start.
            // If session is not in Use, Perform UpSert and reset InUse
            // Also update Sorted session list

            if (Key == null)
            {
                throw new ArgumentNullException("Key");
            }
            if (Session == null)
            {
                throw new ArgumentNullException("Session");
            }

            LockedSessionInfo = null;
            bool tryAgain;

            Diags.ResetDeadLockCounter();
            do
            {
                tryAgain = false;
                AcquireReadLock();
                ISessionObject entry;
                try
                {
                    dict.TryGetValue(Key, out entry);
                }
                finally
                {
                    ReleaseReadLock();
                }

                if (entry == null)
                {
                    if (!UpdateIfNotFound)
                    {
                        return(SessionActionResult.NotFound);
                    }

                    //Session not found -- insert brand new session object
                    AcquireWriteLock();
                    try
                    {
                        //Check again to be sure now that the write-lock has been obtained
                        dict.TryGetValue(Key, out entry);
                        if (entry != null)
                        {
                            //ooops -- another thread inserted a seesion with this key while this thread was trying to obtain the write-lock
                            //so try again
                            tryAgain = true;
                            continue;
                        }

                        Session.LockCookie = 1; //For some reason Lockcookie starts counting from 2 -- so set it to 1 now so that it increments to 2 when sought
                        dict[Key]          = Session;
                        expiryList.Add(DateTime.UtcNow.Add(new TimeSpan(0, Session.TimeOut, 0)), Key, Key);
                        Diags.LogNewSession(Key, Session);
                    }
                    finally
                    {
                        ReleaseWriteLock();
                    }
                }
                else
                {
                    //Session Found

                    if (InsertOnly)
                    {
                        Diags.LogSessionAlreadyExists(Key);
                        return(SessionActionResult.AlreadyExists); //Do not perform an update if InsertOnly is requested
                    }

                    //There is no need to acquire a write lock here since the dictionary is not been modified.
                    //Only the dictionary entry itself is been updated and such updates are guaranteed to be atomic
                    //if the atomic InUse property is set.

                    if (entry.CompareExchangeIsInUse(true, false) == false)
                    {
                        //the InUse flag is set, so this code section has exclusive access to this session object
                        try
                        {
                            if (entry.IsLocked)
                            {
                                if (!entry.UnLock(Session.LockCookie))
                                {
                                    //Lockcookie did not match
                                    LockedSessionInfo = (SessionResponseInfo)entry.CreateResponseInfo();
                                    Diags.LogSessionIsLocked(Key);
                                    return(SessionActionResult.Locked);
                                }
                            }

                            Session.LockCookie = entry.LockCookie;                                              //Overwrite the incoming session's lock-cookie with the internal one's so as not to let external input change the lockcookie
                            Session.ExtraFlags = -1;                                                            //disable extra flags since an update is being performed

                            entry.CopyFrom(Session);                                                            //Copy all information from Session to entry
                            expiryList.Add(DateTime.UtcNow.Add(new TimeSpan(0, Session.TimeOut, 0)), Key, Key); //reset expiry timeout
                            Diags.LogUpdatedSession(Key, Session);
                        }
                        finally
                        {
                            entry.CompareExchangeIsInUse(false, true);
                        }
                    }
                    else
                    {
                        //Is this entry being exported?
                        if (entry.IsExporting)
                        {
                            //This session is already been exported so leave
                            Diags.ResetDeadLockCounter();
                            return(SessionActionResult.Exporting);
                        }


                        //Another thread is using this session and will be done soon so try again

                        Thread.Sleep(1); //pause for 1 ms
                        tryAgain = true;
                    }
                }

                Diags.DetectDeadLock(Key, DeadLockIterationCount); //Signal a deadlock after 2000 iterations
            } while (tryAgain);

            Diags.ResetDeadLockCounter(); //Signal deadlock was freed

            return(SessionActionResult.OK);
        }
Example #11
0
        /// <summary>
        /// Ends an external session export
        /// </summary>
        /// <param name="Key">Session key</param>
        /// <param name="RemoveSession">True to remove session from dictionary</param>
        public void EndExport(string Key, bool RemoveSession)
        {
            //This method resets the inuse property if the isExporting property is true

            if (Key == null)
            {
                throw new ArgumentNullException("Key");
            }

            AcquireReadLock();
            ISessionObject entry;

            try
            {
                dict.TryGetValue(Key, out entry);
            }
            finally
            {
                ReleaseReadLock();
            }

            if (entry == null)
            {
                //Session not found -- it's okay, don't freak out, session may have expired.
                return;
            }
            else
            {
                //Session Found
                if (entry.IsInUse)
                {
                    //The InUse flag, now check the isExporting flag
                    if (!entry.IsExporting)
                    {
                        Exception ex = new InvalidOperationException("EndExport must be called after a call to BeginExport");
                        Diags.LogApplicationError("EndExport must be called after a call to BeginExport -- Entry is InUse but IsExporting is false", ex);
                        throw ex;
                    }

                    try
                    {
                        //Delete session
                        if (RemoveSession)
                        {
                            AcquireWriteLock();
                            try
                            {
                                if (dict.Remove(Key))
                                {
                                    expiryList.Remove(Key);
                                    Diags.LogSessionDeleted(Key);
                                }
                            }
                            finally
                            {
                                ReleaseWriteLock();
                            }
                        }
                    }
                    finally
                    {
                        entry.IsExporting = false;
                        Diags.LogSessionExported(Key);
                        entry.CompareExchangeIsInUse(false, true);
                    }
                }
                else
                {
                    Exception ex = new InvalidOperationException("EndExport must be called after a call to BeginExport");
                    Diags.LogApplicationError("EndExport must be called after a call to BeginExport -- Entry is not in use", ex);
                    throw ex;
                }
            }
        }
Example #12
0
        /// <summary>
        /// Reads a stored session
        /// </summary>
        /// <param name="Key">Session Key</param>
        /// <param name="Reader">Method to call to complete read</param>
        /// <param name="StateObject">State object</param>
        /// <param name="isExporting">Indicates if the session is to be exported</param>
        /// <returns>Result of read action</returns>
        private SessionActionResult Read(string Key, SessionReadHandler Reader, object StateObject, bool isExporting)
        {
            if (Key == null)
            {
                throw new ArgumentNullException("Key");
            }

            bool tryAgain;
            bool sessionIslocked = false;

            Diags.ResetDeadLockCounter(); //Reset Dead lock counter

            do
            {
                tryAgain = false;
                AcquireReadLock();
                ISessionObject entry;
                try
                {
                    dict.TryGetValue(Key, out entry);
                }
                finally
                {
                    ReleaseReadLock();
                }

                if (entry == null)
                {
                    //Session not found
                    Diags.LogSessionNotFound(Key);
                    return(SessionActionResult.NotFound);
                }
                else
                {
                    //Session Found
                    if (entry.CompareExchangeIsInUse(true, false) == false)
                    {
                        //The InUse flag has been set and now this thread has exclusive access to this session object

                        try
                        {
                            //Set IsExporting flag for this entry if item is to be exported
                            if (isExporting)
                            {
                                entry.IsExporting = true;
                            }

                            //Call Reader Delegate
                            if (Reader != null)
                            {
                                Reader(entry, StateObject);
                            }

                            if (isExporting)
                            {
                                Diags.LogSessionExporting(Key, entry);
                            }
                            else
                            {
                                Diags.LogSessionRead(Key, entry);
                            }
                            sessionIslocked = entry.IsLocked;
                        }
                        finally
                        {
                            if (!isExporting) //Remove inUse property if not exporting
                            {
                                entry.CompareExchangeIsInUse(false, true);
                            }
                        }
                    }
                    else
                    {
                        //Nope, it's still there so check if it's been exported and try again

                        if (entry.IsExporting)
                        {
                            //This session is already been exported so leave
                            Diags.ResetDeadLockCounter();
                            return(SessionActionResult.Exporting);
                        }

                        Thread.Sleep(1); //pause for 1 ms
                        tryAgain = true;
                    }

                    Diags.DetectDeadLock(Key, DeadLockIterationCount); //Signal a deadlock after 2000 iterations
                }
            } while (tryAgain);

            Diags.ResetDeadLockCounter(); //Signal deadlock was freed

            if (sessionIslocked && !isExporting)
            {
                Diags.LogSessionIsLocked(Key);
                return(SessionActionResult.Locked);
            }
            else
            {
                return(SessionActionResult.OK);
            }
        }
Example #13
0
        /// <summary>
        /// Removes a session from the dictionary
        /// </summary>
        /// <param name="Key">Session Key</param>
        /// <param name="LockCookie">Lock Cookie</param>
        /// <param name="IsExpiring">Indicates that the item is being removed because it's expiring</param>
        /// <param name="ExpiryDate">The Item expiry date (for comparison)</param>
        /// <param name="LockedSessionInfo">Locked session information if session is locked</param>
        /// <returns>Result of Action</returns>
        private SessionActionResult Remove(string Key, uint LockCookie, bool IsExpiring, DateTime ExpiryDate, out SessionResponseInfo LockedSessionInfo)
        {
            if (Key == null)
            {
                throw new ArgumentNullException("Key");
            }
            LockedSessionInfo = null;

            bool tryAgain;

            Diags.ResetDeadLockCounter();

            do
            {
                tryAgain = false;
                AcquireReadLock();
                ISessionObject entry;
                try
                {
                    dict.TryGetValue(Key, out entry);
                }
                finally
                {
                    ReleaseReadLock();
                }

                if (entry == null)
                {
                    //Session not found
                    Diags.LogSessionNotFound(Key);
                    return(SessionActionResult.NotFound);
                }
                else
                {
                    //Session Found
                    if (entry.CompareExchangeIsInUse(true, false) == false)
                    {
                        try
                        {
                            //The InUse flag is set and so this code section has exclusive access to this session object
                            AcquireWriteLock();

                            try
                            {
                                //Check again to be sure, now that the write-lock has been obtained
                                ISessionObject oldEntry = entry;
                                if (!dict.TryGetValue(Key, out entry))
                                {
                                    //ooops -- another thread deleted the session from the dictionary while this thread
                                    //was either trying to do the compareExchange (or if buggy, while obtaining the write-lock)
                                    //so try again
                                    oldEntry.CompareExchangeIsInUse(false, true); //unlock the previously locked item
                                    tryAgain = true;
                                    continue;
                                }

                                if (IsExpiring)
                                {
                                    DateTime timeStamp;
                                    if (expiryList.TryGetTimeStamp(Key, out timeStamp))
                                    {
                                        if (timeStamp != ExpiryDate)
                                        {
                                            //The expiration date on this session was updated, so leave
                                            return(SessionActionResult.OK);
                                        }
                                    }
                                }

                                if (!IsExpiring && entry.IsLocked) //Locked items DO expire. if not expiring, LockCookie has to match session's
                                {
                                    if (!entry.UnLock(LockCookie))
                                    {
                                        //Lockcookie did not match
                                        LockedSessionInfo = (SessionResponseInfo)entry.CreateResponseInfo();
                                        Diags.LogSessionIsLocked(Key);
                                        return(SessionActionResult.Locked);
                                    }
                                }

                                if (dict.Remove(Key))
                                {
                                    expiryList.Remove(Key);
                                    if (IsExpiring)
                                    {
                                        Diags.LogSessionExpired(Key);
                                    }
                                    else
                                    {
                                        Diags.LogSessionDeleted(Key);
                                    }
                                }
                                else
                                {
                                    //This should never happen
                                    Diags.Fail("ASSERTION Failed -- Session dictionary was unable to remove key\r\n");
                                }
                            }
                            finally
                            {
                                ReleaseWriteLock();
                            }
                        }
                        finally
                        {
                            if (entry != null)
                            {
                                entry.CompareExchangeIsInUse(false, true);
                            }
                        }
                    }
                    else
                    {
                        //Is this entry being exported?
                        if (entry.IsExporting)
                        {
                            //This session is already been exported so leave
                            Diags.ResetDeadLockCounter();
                            return(SessionActionResult.Exporting);
                        }

                        //Another thread is using this session and will be done soon so try again

                        Thread.Sleep(1); //pause for 1 ms
                        tryAgain = true;
                    }

                    Diags.DetectDeadLock(Key, DeadLockIterationCount); //Signal a deadlock after 2000 iterations
                }
            } while (tryAgain);

            Diags.ResetDeadLockCounter(); //Signal deadlock was freed
            return(SessionActionResult.OK);
        }