Example #1
0
 private void EndConnect(IAsyncResult ar)
 {
     if (!ar.CompletedSynchronously)
     {
         ConnectionMultiplexer.TraceWithoutContext("EndConnect (async)");
         EndConnectImpl(ar);
     }
 }
 void IResultBox.ActivateContinuations()
 {
     lock (this)
     { // tell the waiting thread that we're done
         Monitor.PulseAll(this);
     }
     ConnectionMultiplexer.TraceWithoutContext("Pulsed", "Result");
 }
 bool IResultBox.TryComplete(bool isAsync)
 {
     lock (this)
     { // tell the waiting thread that we're done
         Monitor.PulseAll(this);
     }
     ConnectionMultiplexer.TraceWithoutContext("Pulsed", "Result");
     return(true);
 }
        /// <summary>
        /// Compare against a RedisValue for relative order
        /// </summary>
        public int CompareTo(RedisValue other)
        {
            try
            {
                long        thisInt64, otherInt64;
                double      thisDouble, otherDouble;
                CompareType thisType  = this.ResolveType(out thisInt64, out thisDouble),
                            otherType = other.ResolveType(out otherInt64, out otherDouble);

                if (thisType == CompareType.Null)
                {
                    return(otherType == CompareType.Null ? 0 : -1);
                }
                if (otherType == CompareType.Null)
                {
                    return(1);
                }

                if (thisType == CompareType.Int64)
                {
                    if (otherType == CompareType.Int64)
                    {
                        return(thisInt64.CompareTo(otherInt64));
                    }
                    if (otherType == CompareType.Double)
                    {
                        return(((double)thisInt64).CompareTo(otherDouble));
                    }
                }
                else if (thisType == CompareType.Double)
                {
                    if (otherType == CompareType.Int64)
                    {
                        return(thisDouble.CompareTo((double)otherInt64));
                    }
                    if (otherType == CompareType.Double)
                    {
                        return(thisDouble.CompareTo(otherDouble));
                    }
                }
                // otherwise, compare as strings
#if !CORE_CLR
                return(StringComparer.InvariantCulture.Compare((string)this, (string)other));
#else
                var compareInfo = System.Globalization.CultureInfo.InvariantCulture.CompareInfo;
                return(compareInfo.Compare((string)this, (string)other, System.Globalization.CompareOptions.Ordinal));
#endif
            }
            catch (Exception ex)
            {
                ConnectionMultiplexer.TraceWithoutContext(ex.Message);
            }
            // if all else fails, consider equivalent
            return(0);
        }
Example #5
0
 private static void AnyOrderCompletionHandler(object state)
 {
     try
     {
         ConnectionMultiplexer.TraceWithoutContext("Completing async (any order): " + state);
         ((ICompletable)state).TryComplete(true);
     }
     catch (Exception ex)
     {
         ConnectionMultiplexer.TraceWithoutContext("Async completion error: " + ex.Message);
     }
 }
 public bool TryComplete(bool isAsync)
 {
     if (resultBox != null)
     {
         return(resultBox.TryComplete(isAsync));
     }
     else
     {
         ConnectionMultiplexer.TraceWithoutContext("No result-box to complete for " + Command, "Message");
         return(true);
     }
 }
Example #7
0
        public override bool TryComplete(bool isAsync)
        {
            if (stateOrCompletionSource is TaskCompletionSource <T[]> tcs)
            {
                if (countNeeded != values.Count)
                {
                    //true or false?
                    return(true);
                }
                if (isAsync || (tcs.Task.CreationOptions & TaskCreationOptions.RunContinuationsAsynchronously) != 0)
                {
                    // either on the async completion step, or the task is guarded
                    // againsts thread-stealing; complete it directly
                    // (note: RunContinuationsAsynchronously is only usable from NET46)
                    UnwrapAndRecycle(this, true, out Dictionary <Message, T> values, out Exception ex);

                    if (ex == null)
                    {
                        tcs.TrySetResult(values.Values.ToArray());
                    }
                    else
                    {
                        if (ex is TaskCanceledException)
                        {
                            tcs.TrySetCanceled();
                        }
                        else
                        {
                            tcs.TrySetException(ex);
                        }
                        // mark it as observed
                        GC.KeepAlive(tcs.Task.Exception);
                        GC.SuppressFinalize(tcs.Task);
                    }
                    return(true);
                }
                else
                {
                    // could be thread-stealing continuations; push to async to preserve the reader thread
                    return(false);
                }
            }
            else
            {
                lock (this)
                { // tell the waiting thread that we're done
                    Monitor.PulseAll(this);
                }
                ConnectionMultiplexer.TraceWithoutContext("Pulsed", "Result");
                return(true);
            }
        }
Example #8
0
            internal override bool TryValidate(RawResult result, out bool value)
            {
                bool parsed;

                if (ResultProcessor.DemandZeroOrOneProcessor.TryGet(result, out parsed))
                {
                    value = parsed == expectedResult;
                    ConnectionMultiplexer.TraceWithoutContext("exists: " + parsed + "; expected: " + expectedResult + "; voting: " + value);
                    return(true);
                }
                value = false;
                return(false);
            }
        /// <summary>
        /// Compare against a RedisValue for relative order
        /// </summary>
        /// <param name="other">The other <see cref="RedisValue"/> to compare.</param>
        public int CompareTo(RedisValue other)
        {
            try
            {
                CompareType thisType  = ResolveType(out long thisInt64, out double thisDouble),
                            otherType = other.ResolveType(out long otherInt64, out double otherDouble);

                if (thisType == CompareType.Null)
                {
                    return(otherType == CompareType.Null ? 0 : -1);
                }
                if (otherType == CompareType.Null)
                {
                    return(1);
                }

                if (thisType == CompareType.Int64)
                {
                    if (otherType == CompareType.Int64)
                    {
                        return(thisInt64.CompareTo(otherInt64));
                    }
                    if (otherType == CompareType.Double)
                    {
                        return(((double)thisInt64).CompareTo(otherDouble));
                    }
                }
                else if (thisType == CompareType.Double)
                {
                    if (otherType == CompareType.Int64)
                    {
                        return(thisDouble.CompareTo((double)otherInt64));
                    }
                    if (otherType == CompareType.Double)
                    {
                        return(thisDouble.CompareTo(otherDouble));
                    }
                }
                // otherwise, compare as strings
                return(StringComparer.InvariantCulture.Compare((string)this, (string)other));
            }
            catch (Exception ex)
            {
                ConnectionMultiplexer.TraceWithoutContext(ex.Message);
            }
            // if all else fails, consider equivalent
            return(0);
        }
 internal override bool TryValidate(RawResult result, out bool value)
 {
     switch (result.Type)
     {
     case ResultType.BulkString:
     case ResultType.SimpleString:
     case ResultType.Integer:
         var parsed = result.AsRedisValue();
         value = parsed.IsInteger && (expectedLength.CompareTo((long)parsed) == compareToResult);
         ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsed + "; expected: " + expectedLength +
                                                   "; wanted: " + GetComparisonString() + "; voting: " + value);
         return(true);
     }
     value = false;
     return(false);
 }
Example #11
0
 internal override bool TryValidate(RawResult result, out bool value)
 {
     switch (result.Type)
     {
     case ResultType.BulkString:
     case ResultType.SimpleString:
     case ResultType.Integer:
         var parsed = result.AsRedisValue();
         value = (parsed == expectedValue) == expectedEqual;
         ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsed + "; expected: " + (string)expectedValue +
                                                   "; wanted: " + (expectedEqual ? "==" : "!=") + "; voting: " + value);
         return(true);
     }
     value = false;
     return(false);
 }
 public bool TryComplete(bool isAsync)
 {
     if (handler == null)
     {
         return(true);
     }
     if (isAsync)
     {
         ConnectionMultiplexer.TraceWithoutContext("Invoking...: " + (string)channel, "Subscription");
         foreach (Action <RedisChannel, RedisValue> sub in handler.GetInvocationList())
         {
             try { sub.Invoke(channel, message); }
             catch { }
         }
         ConnectionMultiplexer.TraceWithoutContext("Invoke complete", "Subscription");
         return(true);
     }
     // needs to be called async (unless there is nothing to do!)
     return(false);
 }
Example #13
0
 public bool TryComplete(bool isAsync)
 {
     if (resultBox != null)
     {
         var ret = resultBox.TryComplete(isAsync);
         if (performance != null)
         {
             performance.SetCompleted();
         }
         return(ret);
     }
     else
     {
         ConnectionMultiplexer.TraceWithoutContext("No result-box to complete for " + Command, "Message");
         if (performance != null)
         {
             performance.SetCompleted();
         }
         return(true);
     }
 }
Example #14
0
        public bool TryComplete(bool isAsync)
        {
            if (resultBox != null)
            {
                var ret = resultBox.TryComplete(isAsync);

                if (ret && isAsync)
                {
                    resultBox = null; // in async mode TryComplete will have unwrapped and recycled resultBox; ensure we no longer reference it via this message
                }

                performance?.SetCompleted();
                return(ret);
            }
            else
            {
                ConnectionMultiplexer.TraceWithoutContext("No result-box to complete for " + Command, "Message");
                performance?.SetCompleted();
                return(true);
            }
        }
            internal override bool TryValidate(RawResult result, out bool value)
            {
                switch (type)
                {
                case RedisType.SortedSet:
                    var parsedValue = result.AsRedisValue();
                    value = (parsedValue.IsNull != expectedResult);
                    ConnectionMultiplexer.TraceWithoutContext("exists: " + parsedValue + "; expected: " + expectedResult + "; voting: " + value);
                    return(true);

                default:
                    bool parsed;
                    if (ResultProcessor.DemandZeroOrOneProcessor.TryGet(result, out parsed))
                    {
                        value = parsed == expectedResult;
                        ConnectionMultiplexer.TraceWithoutContext("exists: " + parsed + "; expected: " + expectedResult + "; voting: " + value);
                        return(true);
                    }
                    value = false;
                    return(false);
                }
            }
        private void EndConnectImpl(IAsyncResult ar, ConnectionMultiplexer multiplexer, TextWriter log, Tuple <Socket, ISocketCallback> tuple)
        {
            try
            {
                bool ignoreConnect = false;
                ShouldIgnoreConnect(tuple.Item2, ref ignoreConnect);
                if (ignoreConnect)
                {
                    return;
                }
                var socket   = tuple.Item1;
                var callback = tuple.Item2;
#if CORE_CLR
                multiplexer.Wait((Task)ar); // make it explode if invalid (note: already complete at this point)
#else
                socket.EndConnect(ar);
#endif
                var netStream  = new NetworkStream(socket, false);
                var socketMode = callback?.Connected(netStream, log) ?? SocketMode.Abort;
                switch (socketMode)
                {
                case SocketMode.Poll:
                    multiplexer.LogLocked(log, "Starting poll");
                    OnAddRead(socket, callback);
                    break;

                case SocketMode.Async:
                    multiplexer.LogLocked(log, "Starting read");
                    try
                    { callback.StartReading(); }
                    catch (Exception ex)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(ex.Message);
                        Shutdown(socket);
                    }
                    break;

                default:
                    ConnectionMultiplexer.TraceWithoutContext("Aborting socket");
                    Shutdown(socket);
                    break;
                }
            }
            catch (ObjectDisposedException)
            {
                multiplexer.LogLocked(log, "(socket shutdown)");
                if (tuple != null)
                {
                    try
                    { tuple.Item2.Error(); }
                    catch (Exception inner)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(inner.Message);
                    }
                }
            }
            catch (Exception outer)
            {
                ConnectionMultiplexer.TraceWithoutContext(outer.Message);
                if (tuple != null)
                {
                    try
                    { tuple.Item2.Error(); }
                    catch (Exception inner)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(inner.Message);
                    }
                }
            }
        }
 private void UnexpectedResponse(Message message, RawResult result)
 {
     ConnectionMultiplexer.TraceWithoutContext("From " + GetType().Name, "Unexpected Response");
     ConnectionFail(message, ConnectionFailureType.ProtocolFailure, "Unexpected response to " + (message == null ? "n/a" : message.Command.ToString()) + ": " + result.ToString());
 }
        private void EndConnectImpl(IAsyncResult ar, ConnectionMultiplexer multiplexer, TextWriter log)
        {
            Tuple <Socket, ISocketCallback> tuple = null;

            try
            {
                tuple = (Tuple <Socket, ISocketCallback>)ar.AsyncState;
                bool ignoreConnect = false;
                ShouldIgnoreConnect(tuple.Item2, ref ignoreConnect);
                if (ignoreConnect)
                {
                    return;
                }
                var socket   = tuple.Item1;
                var callback = tuple.Item2;
                socket.EndConnect(ar);
                var netStream  = new NetworkStream(socket, false);
                var socketMode = callback == null ? SocketMode.Abort : callback.Connected(netStream, log);
                switch (socketMode)
                {
                case SocketMode.Poll:
                    multiplexer.LogLocked(log, "Starting poll");
                    OnAddRead(socket, callback);
                    break;

                case SocketMode.Async:
                    multiplexer.LogLocked(log, "Starting read");
                    try
                    { callback.StartReading(); }
                    catch (Exception ex)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(ex.Message);
                        Shutdown(socket);
                    }
                    break;

                default:
                    ConnectionMultiplexer.TraceWithoutContext("Aborting socket");
                    Shutdown(socket);
                    break;
                }
            }
            catch (ObjectDisposedException)
            {
                multiplexer.LogLocked(log, "(socket shutdown)");
                if (tuple != null)
                {
                    try
                    { tuple.Item2.Error(); }
                    catch (Exception inner)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(inner.Message);
                    }
                }
            }
            catch (Exception outer)
            {
                ConnectionMultiplexer.TraceWithoutContext(outer.Message);
                if (tuple != null)
                {
                    try
                    { tuple.Item2.Error(); }
                    catch (Exception inner)
                    {
                        ConnectionMultiplexer.TraceWithoutContext(inner.Message);
                    }
                }
            }
        }
Example #19
0
        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
                                {
#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>();

            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();
                }
            }
        }