private void PerformReceive() { fill: // no data to process => read the socket if (socket.ReadBuffer.IsEmpty) { if (LogTraceEnabled) { log.Trace("Read buffer is empty, ask for more."); } socket.ScheduleReceive(success => { if (success) { MarkAsReady(); owner.NeedsIO(this); } else { // this is a soft fail (cannot throw from other thread), // so we requeue for IO and exception will be thrown by Receive() FailMe(new IOException("Failed receiving from " + endpoint)); } }); return; } // process the commands in the readQueue while (readQueue.Count > 0) { // continue filling the previously unfinished response, // or create a new one var response = inprogressResponse ?? CreateResponse(); // continue filling the Response object from the buffer // Read() returns true if further data (IO) is required // (usually when the current response data is larger than the receive buffer size) if (response.Read(socket.ReadBuffer)) { inprogressResponse = response; if (LogTraceEnabled) { log.Trace("Response is not read fully, continue reading from the socket."); } // refill the buffer // TODO if Receive returns synchrously several times, a node with a huge inprogress response can monopolize the IO thread goto fill; } // successfully read a response from the read buffer inprogressResponse = null; var matching = false; while (!matching && readQueue.Count > 0) { var data = readQueue.Peek(); Debug.Assert(!data.IsEmpty); // if the response does not matches the current op, it means it's a // response to later command in the queue, so all commands before it are silent commands // successful silent ops will receive null as response (since we have no real response) // (or we've ran into a bug) matching = data.Op.Handles(response); if (LogTraceEnabled) { log.Trace("Command {0} handles reponse: {1}", data.Op, matching); } // returns false when no more IO is required => command is processed // otherwise continue filling the buffer if (!data.Op.ProcessResponse(matching ? response : null)) { readQueue.Dequeue(); counterReadQueue.Decrement(); counterOpReadPerSec.Increment(); if (trace.IsEnabled()) { trace.DequeueReadOp(name); } if (data.Task != null) { data.Task.TrySetResult(data.Op); } } } response.Dispose(); } // set the node into send mode and requeue for IO Volatile.Write(ref runMode, MODE_SEND); MarkAsReady(); owner.NeedsIO(this); }
private void TryProcessReceivedData() { // process the commands in the readQueue while (readQueue.Count > 0) { // continue filling the previously unfinished response, // or create a new one var response = inprogressResponse ?? CreateResponse(); // continue filling the Response object from the buffer // Read() returns true if further data (IO) is required // (usually when the current response data is larger than the receive buffer size) if (response.Read(socket.ReadBuffer)) { inprogressResponse = response; LogTo.Trace("Response is not read fully, continue reading from the socket."); // refill the buffer _FinishedReading(); owner.NeedsIO(this); return; } // successfully read a response from the read buffer inprogressResponse = null; var isHandled = false; while (!isHandled && readQueue.Count > 0) { var data = readQueue.Peek(); Debug.Assert(!data.IsEmpty); // If the response does not matches the current op, it means it's a // response to a later command in the queue, so all commands before it // were silent commands without a response (== usually success). // So, successful silent ops will receive null as response (since // we have no real response (or we've ran into a bug)) isHandled = data.Op.Handles(response); LogTo.Trace($"Command {data.Op} handles reponse: {isHandled}"); // operations are allowed to hanndle subsequent responses // they returns false when no more IO (response) is required => done processing if (!data.Op.ProcessResponse(isHandled ? response : null)) { readQueue.Dequeue(); data.Task.TrySetResult(data.Op); #region Diagnostics npm.DequeueReadOp(); CoreEventSource.DequeueReadOp(name); #endregion } } response.Dispose(); } LogTo.Trace($"{name} fininshed RECEIVE, unlock read"); _FinishedReading(); }