public bool ScheduleReceive(Action <bool> whenDone) { CoreEventSource.ReceiveStart(name, IsAlive); if (!IsAlive) { whenDone(false); return(false); } if (Interlocked.CompareExchange(ref isReceiving, 1, 0) != 0) { return(false); } recvArgs.UserToken = whenDone; if (!socket.ReceiveAsync(recvArgs)) { // receive was done synchronously ReceiveAsyncCompleted(null, recvArgs); } return(true); }
private void SendAsyncCompleted(object sender, SocketAsyncEventArgs e) { var sent = sendArgs.BytesTransferred; CoreEventSource.SendChunk(name, IsAlive, sent, sendArgs.SocketError); // failed during send if (sendArgs.SocketError != SocketError.Success || sent < 1) { FinishSending(false); return; } var sendOffset = sendArgs.Offset + sent; var sendCount = sendArgs.Count - sent; // OS sent less data than we asked for, so send the remaining data if (sendCount > 0) { PerformSend(sendOffset, sendCount); } else { Debug.Assert(sendCount == 0); // all data was sent sendBuffer.Clear(); FinishSending(true); } }
/// <summary> /// Writes an operation to the output buffer. Handles the case where the op does not fit the buffer fully. /// </summary> /// <param name="data"></param> protected virtual void WriteOp(OpQueueEntry data) { if (currentWriteCopier != null) { throw new InvalidOperationException("Cannot write an operation while another is in progress."); } var request = data.Op.CreateRequest(); if (!request.WriteTo(socket.WriteBuffer)) // no pending IO => fully written { readQueue.Enqueue(data); request.Dispose(); #region Diagnostics npm.EnqueueReadOp(); CoreEventSource.EnqueueReadOp(name); LogTo.Trace($"Full send of {data.Op}"); #endregion } else { // it did not fit into the write buffer, so save the current op // as "in-progress"; DoRun will loop until it's fully sent currentWriteOp = data; currentWriteCopier = request; LogTo.Trace($"Partial send of {data.Op}"); } }
/// <summary> /// Sends the current chunked op. Happens when an op's data cannot fit the write buffer in one pass. /// </summary> /// <returns>returns true if further IO is required; false if no inprogress op present or the last chunk was successfully added to the buffer</returns> private bool ContinueWritingCurrentOp() { // check if we have an op in progress if (currentWriteCopier == null) { return(false); } if (currentWriteCopier.WriteTo(socket.WriteBuffer)) { return(true); } // last chunk was sent LogTo.Trace($"Sent & finished {currentWriteOp.Op}"); // op is sent fully; response can be expected readQueue.Enqueue(currentWriteOp); #region Diagnostics npm.EnqueueReadOp(); CoreEventSource.EnqueueReadOp(name); #endregion // clean up currentWriteCopier.Dispose(); currentWriteCopier = null; currentWriteOp = OpQueueEntry.Empty; return(false); }
private void FinishSending(bool success) { CoreEventSource.SendStop(name, IsAlive, success); Volatile.Write(ref isSending, 0); ((Action <bool>)sendArgs.UserToken)?.Invoke(success); }
private void ReceiveAsyncCompleted(object sender, SocketAsyncEventArgs recvArgs) { var received = recvArgs.BytesTransferred; CoreEventSource.ReceiveChunk(name, IsAlive, received, recvArgs.SocketError); var success = recvArgs.SocketError == SocketError.Success && received > 0; recvBuffer.SetDataLength(success ? received : 0); CoreEventSource.ReceiveStop(name, IsAlive, success); Volatile.Write(ref isReceiving, 0); ((Action <bool>)recvArgs.UserToken).Invoke(success); }
/// <summary> /// Cleans up a ConcurrentQueue, marking all items as failed /// </summary> private void FailQueue(ConcurrentQueue <OpQueueEntry> queue, Exception e) { OpQueueEntry data; while (queue.TryDequeue(out data)) { var t = data.Task; if (t != null) { t.SetException(e); } npm.Error(); CoreEventSource.NodeError(name); } }
protected virtual OpQueueEntry GetNextOp() { OpQueueEntry data; if (writeQueue.TryDequeue(out data)) { #region Diagnostics npm.DequeueWriteOp(); CoreEventSource.DequeueWriteOp(name); #endregion return(data); } return(OpQueueEntry.Empty); }
/// <summary> /// Cleans up an AdvQueue, marking all items as failed /// </summary> private void FailQueue(Queue <OpQueueEntry> queue, Exception e) { foreach (var data in queue) { var t = data.Task; if (t == null) { break; } t.TrySetException(e); npm.Error(); CoreEventSource.NodeError(name); } queue.Clear(); }
private void PerformConnect(IPEndPoint endpoint, CancellationToken token) { this.endpoint = endpoint; this.name = endpoint.ToString(); this.IsAlive = false; var sw = Stopwatch.StartNew(); InitBuffers(); using (var mre = new ManualResetEventSlim(false)) using (var opt = new SocketAsyncEventArgs { RemoteEndPoint = endpoint }) { CoreEventSource.ConnectStart(name); opt.Completed += (a, b) => mre.Set(); RecreateSocket(); try { if (socket.ConnectAsync(opt) && !mre.Wait((int)ConnectionTimeout.TotalMilliseconds, token)) { CoreEventSource.ConnectFail(name, SocketError.TimedOut); Socket.CancelConnectAsync(opt); throw new TimeoutException($"Connection timeout {ConnectionTimeout} has been exceeded while trying to connect to {endpoint}"); } if (opt.SocketError != SocketError.Success) { CoreEventSource.ConnectFail(name, opt.SocketError); throw new IOException($"Could not connect to {endpoint}"); } IsAlive = true; } finally { LogTo.Info($"Connected to {endpoint} in {sw.ElapsedMilliseconds} msec"); } } }
public bool ScheduleSend(Action <bool> whenDone) { CoreEventSource.SendStart(name, IsAlive, sendBuffer.Position); if (!IsAlive) { whenDone(false); return(false); } if (Interlocked.CompareExchange(ref isSending, 1, 0) != 0) { return(false); } sendArgs.UserToken = whenDone; PerformSend(sendBuffer.BufferOffset, sendBuffer.Position); return(true); }
private void PerformSend(int sendOffset, int sendCount) { for (;;) { // try sending all our data sendArgs.SetBuffer(sendOffset, sendCount); // send is being done asynchrously (SendAsyncCompleted will clean up) if (socket.SendAsync(sendArgs)) { break; } // send was done synchronously var sent = sendArgs.BytesTransferred; CoreEventSource.SendChunk(name, IsAlive, sent, sendArgs.SocketError); // check for fail if (sendArgs.SocketError != SocketError.Success || sent < 1) { // socket error FinishSending(false); break; } sendOffset += sent; sendCount -= sent; Debug.Assert(sendCount >= 0); // no data is remaining: quit // otherwise try sending a new chunk if (sendCount == 0) { sendBuffer.Clear(); FinishSending(true); break; } } }
public virtual Task <IOperation> Enqueue(IOperation op) { var tcs = new TaskCompletionSource <IOperation>(); npm.NewOp(); try { if (IsAlive) { writeQueue.Enqueue(new OpQueueEntry(op, tcs)); #region Diagnostics npm.EnqueueWriteOp(); CoreEventSource.EnqueueWriteOp(name); #endregion } else { tcs.TrySetException(new IOException(endpoint + " is not alive")); #region Diagnostics npm.Error(); CoreEventSource.NodeError(name); #endregion } } catch (Exception e) { tcs.TrySetException(new IOException(endpoint + " enqueue failed. See inner excption for details.", e)); #region Diagnostics npm.Error(); CoreEventSource.NodeError(name); #endregion } return(tcs.Task); }
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(); }