private void ReadImpl() { List <IntPtr> dead = null, active = new List <IntPtr>(); var activeCallbacks = new List <ISocketCallback>(); IntPtr[] readSockets = EmptyPointers, errorSockets = EmptyPointers; long lastHeartbeat = Environment.TickCount; SocketPair[] allSocketPairs = null; while (true) { managerState = ManagerState.CheckForHeartbeat; active.Clear(); activeCallbacks.Clear(); dead?.Clear(); // this check is actually a pace-maker; sometimes the Timer callback stalls for // extended periods of time, which can cause socket disconnect long now = Environment.TickCount; if (unchecked (now - lastHeartbeat) >= 15000) { managerState = ManagerState.ExecuteHeartbeat; lastHeartbeat = now; lock (socketLookup) { if (allSocketPairs == null || allSocketPairs.Length != socketLookup.Count) { allSocketPairs = new SocketPair[socketLookup.Count]; } socketLookup.Values.CopyTo(allSocketPairs, 0); } foreach (var pair in allSocketPairs) { var callback = pair.Callback; if (callback != null) { try { callback.OnHeartbeat(); } catch { } } } } managerState = ManagerState.LocateActiveSockets; lock (socketLookup) { if (isDisposed) { return; } if (socketLookup.Count == 0) { // if empty, give it a few seconds chance before exiting managerState = ManagerState.NoSocketsPause; Monitor.Wait(socketLookup, TimeSpan.FromSeconds(20)); if (socketLookup.Count == 0) { return; // nothing new came in, so exit } } managerState = ManagerState.PrepareActiveSockets; foreach (var pair in socketLookup) { var socket = pair.Value.Socket; if (socket.Handle == pair.Key && socket.Connected) { if (pair.Value.Socket.Connected) { active.Add(pair.Key); activeCallbacks.Add(pair.Value.Callback); } else { (dead ?? (dead = new List <IntPtr>())).Add(pair.Key); } } } if (dead != null && dead.Count != 0) { managerState = ManagerState.CullDeadSockets; foreach (var socket in dead) { socketLookup.Remove(socket); } } } int pollingSockets = active.Count; if (pollingSockets == 0) { // nobody had actual sockets; just sleep managerState = ManagerState.NoActiveSocketsPause; Thread.Sleep(10); continue; } if (readSockets.Length < active.Count + 1) { managerState = ManagerState.GrowingSocketArray; ConnectionMultiplexer.TraceWithoutContext("Resizing socket array for " + active.Count + " sockets"); readSockets = new IntPtr[active.Count + 6]; // leave so space for growth errorSockets = new IntPtr[active.Count + 6]; } managerState = ManagerState.CopyingPointersForSelect; readSockets[0] = errorSockets[0] = (IntPtr)active.Count; active.CopyTo(readSockets, 1); active.CopyTo(errorSockets, 1); int ready; try { var timeout = new TimeValue(1000); managerState = ManagerState.ExecuteSelect; ready = select(0, readSockets, null, errorSockets, ref timeout); managerState = ManagerState.ExecuteSelectComplete; if (ready <= 0) // -ve typically means a socket was disposed just before; just retry { bool hasWorkToDo = false; if (ready == 0) { managerState = ManagerState.CheckForStaleConnections; foreach (var s in activeCallbacks) { if (s.IsDataAvailable) { hasWorkToDo = true; } else { #pragma warning disable 0420 s.CheckForStaleConnection(ref managerState); #pragma warning restore 0420 } } managerState = ManagerState.CheckForStaleConnectionsDone; } else { lastErrorTicks = Environment.TickCount; } if (!hasWorkToDo) { continue; } } ConnectionMultiplexer.TraceWithoutContext((int)readSockets[0] != 0, "Read sockets: " + (int)readSockets[0]); ConnectionMultiplexer.TraceWithoutContext((int)errorSockets[0] != 0, "Error sockets: " + (int)errorSockets[0]); } catch (Exception ex) { // this typically means a socket was disposed just before; just retry Trace.WriteLine(ex.Message); continue; } bool haveWork = false; int queueCount = (int)readSockets[0]; if (queueCount != 0) { managerState = ManagerState.EnqueueRead; lock (readQueue) { for (int i = 1; i <= queueCount; i++) { var callback = GetCallback(readSockets[i]); if (callback != null) { readQueue.Enqueue(callback); haveWork = true; } } } } queueCount = (int)errorSockets[0]; if (queueCount != 0) { managerState = ManagerState.EnqueueError; lock (errorQueue) { for (int i = 1; i <= queueCount; i++) { var callback = GetCallback(errorSockets[i]); if (callback != null) { errorQueue.Enqueue(callback); haveWork = true; } } } } if (!haveWork) { // edge case: select is returning 0, but data could still be available managerState = ManagerState.EnqueueReadFallback; lock (readQueue) { foreach (var callback in activeCallbacks) { if (callback.IsDataAvailable) { readQueue.Enqueue(callback); } } } } if (ready >= 5) // number of sockets we should attempt to process by ourself before asking for help { // seek help, work in parallel, then synchronize var obj = new QueueDrainSyncLock(this); lock (obj) { managerState = ManagerState.RequestAssistance; ThreadPool.QueueUserWorkItem(HelpProcessItems, obj); managerState = ManagerState.ProcessQueues; ProcessItems(true); if (!obj.Consume()) { // then our worker arrived and picked up work; we need // to let it finish; note that if it *didn't* get that far // yet, the Consume() call will mean that it never tries Monitor.Wait(obj); } } } else { // just do it ourself managerState = ManagerState.ProcessQueues; ProcessItems(true); } } }
private void ReadImpl() { List<IntPtr> dead = null, active = new List<IntPtr>(); List<ISocketCallback> activeCallbacks = new List<ISocketCallback>(); IntPtr[] readSockets = EmptyPointers, errorSockets = EmptyPointers; long lastHeartbeat = Environment.TickCount; SocketPair[] allSocketPairs = null; while (true) { managerState = ManagerState.CheckForHeartbeat; active.Clear(); activeCallbacks.Clear(); if (dead != null) dead.Clear(); // this check is actually a pace-maker; sometimes the Timer callback stalls for // extended periods of time, which can cause socket disconnect long now = Environment.TickCount; if (unchecked(now - lastHeartbeat) >= 15000) { managerState = ManagerState.ExecuteHeartbeat; lastHeartbeat = now; lock (socketLookup) { if (allSocketPairs == null || allSocketPairs.Length != socketLookup.Count) allSocketPairs = new SocketPair[socketLookup.Count]; socketLookup.Values.CopyTo(allSocketPairs, 0); } foreach (var pair in allSocketPairs) { var callback = pair.Callback; if (callback != null) try { callback.OnHeartbeat(); } catch { } } } managerState = ManagerState.LocateActiveSockets; lock (socketLookup) { if (isDisposed) return; if (socketLookup.Count == 0) { // if empty, give it a few seconds chance before exiting managerState = ManagerState.NoSocketsPause; Monitor.Wait(socketLookup, TimeSpan.FromSeconds(20)); if (socketLookup.Count == 0) return; // nothing new came in, so exit } managerState = ManagerState.PrepareActiveSockets; foreach (var pair in socketLookup) { var socket = pair.Value.Socket; if (socket.Handle == pair.Key && socket.Connected) if (pair.Value.Socket.Connected) { active.Add(pair.Key); activeCallbacks.Add(pair.Value.Callback); } else { (dead ?? (dead = new List<IntPtr>())).Add(pair.Key); } } if (dead != null && dead.Count != 0) { managerState = ManagerState.CullDeadSockets; foreach (var socket in dead) socketLookup.Remove(socket); } } int pollingSockets = active.Count; if (pollingSockets == 0) { // nobody had actual sockets; just sleep managerState = ManagerState.NoActiveSocketsPause; Thread.Sleep(10); continue; } if (readSockets.Length < active.Count + 1) { managerState = ManagerState.GrowingSocketArray; ConnectionMultiplexer.TraceWithoutContext("Resizing socket array for " + active.Count + " sockets"); readSockets = new IntPtr[active.Count + 6]; // leave so space for growth errorSockets = new IntPtr[active.Count + 6]; } managerState = ManagerState.CopyingPointersForSelect; readSockets[0] = errorSockets[0] = (IntPtr)active.Count; active.CopyTo(readSockets, 1); active.CopyTo(errorSockets, 1); int ready; try { var timeout = new TimeValue(1000); managerState = ManagerState.ExecuteSelect; ready = select(0, readSockets, null, errorSockets, ref timeout); managerState = ManagerState.ExecuteSelectComplete; if (ready <= 0) // -ve typically means a socket was disposed just before; just retry { bool hasWorkToDo = false; if (ready == 0) { managerState = ManagerState.CheckForStaleConnections; foreach (var s in activeCallbacks) { if (s.IsDataAvailable) { hasWorkToDo = true; } else { s.CheckForStaleConnection(); } } } else { lastErrorTicks = Environment.TickCount; } if (!hasWorkToDo) { continue; } } ConnectionMultiplexer.TraceWithoutContext((int)readSockets[0] != 0, "Read sockets: " + (int)readSockets[0]); ConnectionMultiplexer.TraceWithoutContext((int)errorSockets[0] != 0, "Error sockets: " + (int)errorSockets[0]); } catch (Exception ex) { // this typically means a socket was disposed just before; just retry Trace.WriteLine(ex.Message); continue; } bool haveWork = false; int queueCount = (int)readSockets[0]; if (queueCount != 0) { managerState = ManagerState.EnqueueRead; lock (readQueue) { for (int i = 1; i <= queueCount; i++) { var callback = GetCallback(readSockets[i]); if (callback != null) { readQueue.Enqueue(callback); haveWork = true; } } } } queueCount = (int)errorSockets[0]; if (queueCount != 0) { managerState = ManagerState.EnqueueError; lock (errorQueue) { for (int i = 1; i <= queueCount; i++) { var callback = GetCallback(errorSockets[i]); if (callback != null) { errorQueue.Enqueue(callback); haveWork = true; } } } } if(!haveWork) { // edge case: select is returning 0, but data could still be available managerState = ManagerState.EnqueueReadFallback; lock (readQueue) { foreach (var callback in activeCallbacks) { if(callback.IsDataAvailable) { readQueue.Enqueue(callback); } } } } if (ready >= 5) // number of sockets we should attempt to process by ourself before asking for help { // seek help, work in parallel, then synchronize var obj = new QueueDrainSyncLock(this); lock (obj) { managerState = ManagerState.RequestAssistance; ThreadPool.QueueUserWorkItem(HelpProcessItems, obj); managerState = ManagerState.ProcessQueues; ProcessItems(true); if (!obj.Consume()) { // then our worker arrived and picked up work; we need // to let it finish; note that if it *didn't* get that far // yet, the Consume() call will mean that it never tries Monitor.Wait(obj); } } } else { // just do it ourself managerState = ManagerState.ProcessQueues; ProcessItems(true); } } }
private void ReadImpl() { List <IntPtr> dead = null, active = new List <IntPtr>(); IntPtr[] readSockets = EmptyPointers, errorSockets = EmptyPointers; long lastHeartbeat = Environment.TickCount; SocketPair[] allSocketPairs = null; while (true) { active.Clear(); if (dead != null) { dead.Clear(); } // this check is actually a pace-maker; sometimes the Timer callback stalls for // extended periods of time, which can cause socket disconnect long now = Environment.TickCount; if (unchecked (now - lastHeartbeat) >= 15000) { lastHeartbeat = now; lock (socketLookup) { if (allSocketPairs == null || allSocketPairs.Length != socketLookup.Count) { allSocketPairs = new SocketPair[socketLookup.Count]; } socketLookup.Values.CopyTo(allSocketPairs, 0); } foreach (var pair in allSocketPairs) { var callback = pair.Callback; if (callback != null) { try { callback.OnHeartbeat(); } catch { } } } } lock (socketLookup) { if (isDisposed) { return; } if (socketLookup.Count == 0) { // if empty, give it a few seconds chance before exiting Monitor.Wait(socketLookup, TimeSpan.FromSeconds(20)); if (socketLookup.Count == 0) { return; // nothing new came in, so exit } } foreach (var pair in socketLookup) { var socket = pair.Value.Socket; if (socket.Handle == pair.Key && socket.Connected) { if (pair.Value.Socket.Connected) { active.Add(pair.Key); } else { (dead ?? (dead = new List <IntPtr>())).Add(pair.Key); } } } if (dead != null && dead.Count != 0) { foreach (var socket in dead) { socketLookup.Remove(socket); } } } int pollingSockets = active.Count; if (pollingSockets == 0) { // nobody had actual sockets; just sleep Thread.Sleep(10); continue; } if (readSockets.Length < active.Count + 1) { ConnectionMultiplexer.TraceWithoutContext("Resizing socket array for " + active.Count + " sockets"); readSockets = new IntPtr[active.Count + 6]; // leave so space for growth errorSockets = new IntPtr[active.Count + 6]; } readSockets[0] = errorSockets[0] = (IntPtr)active.Count; active.CopyTo(readSockets, 1); active.CopyTo(errorSockets, 1); int ready; try { var timeout = new TimeValue(1000); ready = select(0, readSockets, null, errorSockets, ref timeout); if (ready <= 0) { continue; // -ve typically means a socket was disposed just before; just retry } ConnectionMultiplexer.TraceWithoutContext((int)readSockets[0] != 0, "Read sockets: " + (int)readSockets[0]); ConnectionMultiplexer.TraceWithoutContext((int)errorSockets[0] != 0, "Error sockets: " + (int)errorSockets[0]); } catch (Exception ex) { // this typically means a socket was disposed just before; just retry Trace.WriteLine(ex.Message); continue; } int queueCount = (int)readSockets[0]; lock (readQueue) { for (int i = 1; i <= queueCount; i++) { readQueue.Enqueue(readSockets[i]); } } queueCount = (int)errorSockets[0]; lock (errorQueue) { for (int i = 1; i <= queueCount; i++) { errorQueue.Enqueue(errorSockets[i]); } } if (ready >= 5) // number of sockets we should attempt to process by ourself before asking for help { // seek help, work in parallel, then synchronize var obj = new QueueDrainSyncLock(this); lock (obj) { ThreadPool.QueueUserWorkItem(HelpProcessItems, obj); ProcessItems(); if (!obj.Consume()) { // then our worker arrived and picked up work; we need // to let it finish; note that if it *didn't* get that far // yet, the Consume() call will mean that it never tries Monitor.Wait(obj); } } } else { // just do it ourself ProcessItems(); } } }