/// <summary> /// It callbacks all operations already sent / or to be written, that do not have a response. /// Invoked from an IO Thread or a pool thread /// </summary> internal void CancelPending(Exception ex, SocketError?socketError = null) { _isCanceled = true; var wasClosed = Interlocked.Exchange(ref _writeState, Connection.WriteStateClosed) == Connection.WriteStateClosed; if (!wasClosed) { if (Closing != null) { Closing(this); } Connection.Logger.Info("Cancelling in Connection {0}, {1} pending operations and write queue {2}", EndPoint.EndpointFriendlyName, InFlight, _writeQueue.Count); if (socketError != null) { Connection.Logger.Verbose("The socket status received was {0}", socketError.Value); } } if (ex == null || ex is ObjectDisposedException) { if (socketError != null) { ex = new SocketException((int)socketError.Value); } else { //It is closing ex = new SocketException((int)SocketError.NotConnected); } } // Dequeue all the items in the write queue var ops = new LinkedList <OperationState>(); OperationState state; while (_writeQueue.TryDequeue(out state)) { ops.AddLast(state); } // Remove every pending operation while (!_pendingOperations.IsEmpty) { Interlocked.MemoryBarrier(); // Remove using a snapshot of the keys var keys = _pendingOperations.Keys.ToArray(); foreach (var key in keys) { if (_pendingOperations.TryRemove(key, out state)) { ops.AddLast(state); } } } Interlocked.MemoryBarrier(); OperationState.CallbackMultiple(ops, RequestError.CreateClientError(ex, false), GetTimestamp()); Interlocked.Exchange(ref _inFlight, 0); }
/// <inheritdoc /> public OperationState Send(IRequest request, Action <IRequestError, Response> callback, int timeoutMillis) { if (_isCanceled) { // Avoid calling back before returning Task.Factory.StartNew(() => callback(RequestError.CreateClientError(new SocketException((int)SocketError.NotConnected), true), null), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); return(null); } IncrementInFlight(); var state = new OperationState( callback, request, timeoutMillis, _connectionObserver.CreateOperationObserver() ); _writeQueue.Enqueue(state); if (state.TimeoutMillis > 0) { // timer can be disposed while connection cancellation hasn't been invoked yet try { var requestTimeout = Configuration.Timer.NewTimeout(OnTimeout, state, state.TimeoutMillis); state.SetTimeout(requestTimeout); } catch (Exception ex) { // Avoid calling back before returning Task.Factory.StartNew(() => callback(RequestError.CreateClientError(ex, true), null), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); return(null); } } RunWriteQueue(); return(state); }
/// <inheritdoc /> public OperationState Send(IRequest request, Action <IRequestError, Response> callback, int timeoutMillis) { if (_isCanceled) { // Avoid calling back before returning Task.Factory.StartNew(() => callback(RequestError.CreateClientError(new SocketException((int)SocketError.NotConnected), true), null), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); return(null); } IncrementInFlight(); var state = new OperationState( callback, request, timeoutMillis, _connectionObserver.CreateOperationObserver() ); _writeQueue.Enqueue(state); RunWriteQueue(); return(state); }
/// <summary> /// Returns an action that capture the parameters closure /// </summary> private Action <MemoryStream, long> CreateResponseAction( ResultMetadata resultMetadata, ISerializer serializer, FrameHeader header, Action <IRequestError, Response, long> callback) { var compressor = Compressor; void DeserializeResponseStream(MemoryStream stream, long timestamp) { Response response = null; IRequestError error = null; var nextPosition = stream.Position + header.BodyLength; try { Stream plainTextStream = stream; if (header.Flags.HasFlag(HeaderFlags.Compression)) { plainTextStream = compressor.Decompress(new WrappedStream(stream, header.BodyLength)); plainTextStream.Position = 0; } response = FrameParser.Parse(new Frame(header, plainTextStream, serializer, resultMetadata)); } catch (Exception caughtException) { error = RequestError.CreateClientError(caughtException, false); } if (response is ErrorResponse errorResponse) { error = RequestError.CreateServerError(errorResponse); response = null; } //We must advance the position of the stream manually in case it was not correctly parsed stream.Position = nextPosition; callback(error, response, timestamp); } return(DeserializeResponseStream); }
private void RunWriteQueueAction() { //Dequeue all items until threshold is passed long totalLength = 0; RecyclableMemoryStream stream = null; var timestamp = GetTimestamp(); while (totalLength < Connection.CoalescingThreshold) { OperationState state = null; while (_writeQueue.TryDequeue(out var tempState)) { if (tempState.CanBeWritten()) { state = tempState; break; } DecrementInFlight(); } if (state == null) { //No more items in the write queue break; } if (!_freeOperations.TryPop(out short streamId)) { //Queue it up for later. _writeQueue.Enqueue(state); //When receiving the next complete message, we can process it. Connection.Logger.Info("Enqueued, no streamIds available. If this message is recurrent consider configuring more connections per host or lower the pressure"); break; } Connection.Logger.Verbose("Sending #{0} for {1} to {2}", streamId, state.Request.GetType().Name, EndPoint.EndpointFriendlyName); if (_isCanceled) { DecrementInFlight(); state.InvokeCallback(RequestError.CreateClientError(new SocketException((int)SocketError.NotConnected), true), timestamp); break; } _pendingOperations.AddOrUpdate(streamId, state, (k, oldValue) => state); var startLength = stream?.Length ?? 0; try { //lazy initialize the stream stream = stream ?? (RecyclableMemoryStream)Configuration.BufferPool.GetStream(Connection.StreamWriteTag); var frameLength = state.WriteFrame(streamId, stream, Serializer, timestamp); _connectionObserver.OnBytesSent(frameLength); totalLength += frameLength; } catch (Exception ex) { //There was an error while serializing or begin sending Connection.Logger.Error(ex); //The request was not written, clear it from pending operations RemoveFromPending(streamId); //Callback with the Exception state.InvokeCallback(RequestError.CreateClientError(ex, true), timestamp); //Reset the stream to before we started writing this frame stream?.SetLength(startLength); break; } } if (totalLength == 0L) { // Nothing to write, set the queue as not running Interlocked.CompareExchange(ref _writeState, Connection.WriteStateInit, Connection.WriteStateRunning); // Until now, we were preventing other threads to running the queue. // Check if we can now write: // a read could have finished (freeing streamIds) or new request could have been added to the queue if (!_freeOperations.IsEmpty && !_writeQueue.IsEmpty) { //The write queue is not empty //An item was added to the queue but we were running: try to launch a new queue RunWriteQueue(); } if (stream != null) { //The stream instance could be created if there was an exception while generating the frame stream.Dispose(); } return; } //Write and close the stream when flushed // ReSharper disable once PossibleNullReferenceException : if totalLength > 0 the stream is initialized _tcpSocket.Write(stream, () => stream.Dispose()); }