/// <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)
            {
                Closing?.Invoke(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);
        }
Exemple #3
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);
            RunWriteQueue();
            return(state);
        }
Exemple #4
0
        /// <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());
        }