예제 #1
0
        /// <summary>
        /// Removes all pending write operations, and fail them with the given <see cref="Exception"/>. The messages
        /// will be released via <see cref="ReferenceCountUtil.SafeRelease(object)"/>.
        /// </summary>
        /// <param name="cause">The <see cref="Exception"/> to fail with.</param>
        public void RemoveAndFailAll(Exception cause)
        {
            Debug.Assert(_ctx.Executor.InEventLoop);
            if (cause is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.cause);
            }

            // It is possible for some of the failed promises to trigger more writes. The new writes
            // will "revive" the queue, so we need to clean them up until the queue is empty.
            for (PendingWrite write = _head; write is object; write = _head)
            {
                _head  = _tail = null;
                _size  = 0;
                _bytes = 0;
                while (write is object)
                {
                    PendingWrite next = write.Next;
                    ReferenceCountUtil.SafeRelease(write.Msg);
                    IPromise promise = write.Promise;
                    Recycle(write, false);
                    Util.SafeSetFailure(promise, cause, Logger);
                    write = next;
                }
            }
            AssertEmpty();
        }
예제 #2
0
        /**
         * Add the given {@code msg} and {@link ChannelPromise}.
         */

        public Task Add(object msg)
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);
            Contract.Requires(msg != null);

            int messageSize = this.estimatorHandle.Size(msg);

            if (messageSize < 0)
            {
                // Size may be unknow so just use 0
                messageSize = 0;
            }
            var          promise     = new TaskCompletionSource();
            PendingWrite write       = PendingWrite.NewInstance(msg, messageSize, promise);
            PendingWrite currentTail = this.tail;

            if (currentTail == null)
            {
                this.tail = this.head = write;
            }
            else
            {
                currentTail.Next = write;
                this.tail        = write;
            }
            this.size++;
            // We need to guard against null as channel.Unsafe.OutboundBuffer may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            this.buffer?.IncrementPendingOutboundBytes(write.Size);
            return(promise.Task);
        }
예제 #3
0
        void Recycle(PendingWrite write, bool update)
        {
            PendingWrite next      = write.Next;
            long         writeSize = write.Size;

            if (update)
            {
                if (next == null)
                {
                    // Handled last PendingWrite so rest head and tail
                    // Guard against re-entrance by directly reset
                    this.head = this.tail = null;
                    this.size = 0;
                }
                else
                {
                    this.head = next;
                    this.size--;
                    Contract.Assert(this.size > 0);
                }
            }

            write.Recycle();
            // We need to guard against null as channel.unsafe().outboundBuffer() may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            this.buffer?.DecrementPendingOutboundBytes(writeSize);
        }
예제 #4
0
        public override Task WriteAsync(IChannelHandlerContext context, object message)
        {
            var pendingWrite = new PendingWrite(message);

            this.queue.Enqueue(pendingWrite);
            return(pendingWrite.PendingTask);
        }
예제 #5
0
        /// <summary>
        /// Adds the given message to this <see cref="PendingWriteQueue"/>.
        /// </summary>
        /// <param name="msg">The message to add to the <see cref="PendingWriteQueue"/>.</param>
        /// <param name="promise"></param>
        public void Add(object msg, IPromise promise)
        {
            Debug.Assert(_ctx.Executor.InEventLoop);
            if (msg is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.msg);
            }
            if (promise is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.promise);
            }

            // It is possible for writes to be triggered from removeAndFailAll(). To preserve ordering,
            // we should add them to the queue and let removeAndFailAll() fail them later.
            int messageSize = GetSize(msg);

            PendingWrite write       = PendingWrite.NewInstance(msg, messageSize, promise);
            PendingWrite currentTail = _tail;

            if (currentTail is null)
            {
                _tail = _head = write;
            }
            else
            {
                currentTail.Next = write;
                _tail            = write;
            }
            _size++;
            _bytes += messageSize;
            _tracker.IncrementPendingOutboundBytes(write.Size);
        }
예제 #6
0
        void Discard(Exception cause = null)
        {
            for (;;)
            {
                PendingWrite current = this.currentWrite;
                if (this.currentWrite == null)
                {
                    current = this.queue.Count > 0 ? this.queue.Dequeue() : null;
                }
                else
                {
                    this.currentWrite = null;
                }

                if (current == null)
                {
                    break;
                }

                object message = current.Message;
                var    chunks  = message as IChunkedInput <T>;
                if (chunks != null)
                {
                    try
                    {
                        if (!chunks.IsEndOfInput)
                        {
                            if (cause == null)
                            {
                                cause = new ClosedChannelException();
                            }

                            current.Fail(cause);
                        }
                        else
                        {
                            current.Success();
                        }
                    }
                    catch (Exception exception)
                    {
                        current.Fail(exception);
                        Logger.Warn($"{StringUtil.SimpleClassName(typeof(ChunkedWriteHandler<T>))}.IsEndOfInput failed", exception);
                    }
                    finally
                    {
                        CloseInput(chunks);
                    }
                }
                else
                {
                    if (cause == null)
                    {
                        cause = new ClosedChannelException();
                    }

                    current.Fail(cause);
                }
            }
        }
예제 #7
0
 public void Recycle()
 {
     this.Size    = 0;
     this.Next    = null;
     this.Msg     = null;
     this.Promise = null;
     this.handle.Release(this);
 }
 public void Recycle()
 {
     this.Size = 0;
     this.Next = null;
     this.Messages.Clear();
     this.Promise = null;
     this.handle.Release(this);
 }
            public static PendingWrite NewInstance(object msg, int size, TaskCompletionSource promise)
            {
                PendingWrite write = Pool.Take();

                write.Add(msg, size);
                write.Promise = promise;
                return(write);
            }
예제 #10
0
 /** connection is closing but a write has to be finished first */
 private Receive ClosingWithPendingWrite(ConnectionInfo info, IActorRef closeCommandor,
                                         Tcp.ConnectionClosed closedEvent)
 {
     return(message =>
     {
         if (message is Tcp.SuspendReading)
         {
             SuspendReading(info);
             return true;
         }
         if (message is Tcp.ResumeReading)
         {
             ResumeReading(info);
             return true;
         }
         if (message is SelectionHandler.ChannelReadable)
         {
             DoRead(info, closeCommandor);
             return true;
         }
         if (message is SelectionHandler.ChannelWritable)
         {
             DoWrite(info);
             if (!WritePending())    // writing is now finished
             {
                 HandleClose(info, closeCommandor, closedEvent);
             }
             return true;
         }
         var updatePendingWrite = message as UpdatePendingWriteAndThen;
         if (updatePendingWrite != null)
         {
             _pendingWrite = updatePendingWrite.RemainingWrite;
             updatePendingWrite.Work();
             if (WritePending())
             {
                 info.Registration.EnableInterest(SocketAsyncOperation.Send);
             }
             else
             {
                 HandleClose(info, closeCommandor, closedEvent);
             }
             return true;
         }
         var writeFailed = message as WriteFileFailed;
         if (writeFailed != null)
         {
             HandleError(info.Handler, writeFailed.E);  // rethrow exception from dispatcher task
             return true;
         }
         if (message is Tcp.Abort)
         {
             HandleClose(info, Sender, IO.Tcp.Aborted.Instance);
             return true;
         }
         return false;
     });
 }
예제 #11
0
        /// <summary>Add the given <c>msg</c> and returns <see cref="Task" /> for completion of processing <c>msg</c>.</summary>
        public void Add(object msg, IPromise promise)
        {
            Debug.Assert(_ctx.Executor.InEventLoop);
            if (msg is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.msg);
            }

            int messageSize = _estimatorHandle.Size(msg);

            if (messageSize < 0)
            {
                // Size may be unknow so just use 0
                messageSize = 0;
            }
            PendingWrite currentTail = _tail;

            if (currentTail is object)
            {
                bool canBundle = CanBatch(msg, messageSize, currentTail.Size);
                if (canBundle)
                {
                    currentTail.Add(msg, messageSize);
                    if (!promise.IsVoid)
                    {
                        currentTail.Promise.Task.LinkOutcome(promise);
                    }
                    return;
                }
            }

            PendingWrite write;

            if (promise.IsVoid || promise is SimplePromiseAggregator)
            {
                var headPromise = _ctx.NewPromise();
                headPromise.Task.LinkOutcome(promise);
                write = PendingWrite.NewInstance(msg, messageSize, headPromise);
            }
            else
            {
                write = PendingWrite.NewInstance(msg, messageSize, promise);
            }
            if (currentTail is null)
            {
                _tail = _head = write;
            }
            else
            {
                currentTail.Next = write;
                _tail            = write;
            }
            _size++;
            // We need to guard against null as channel.Unsafe.OutboundBuffer may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            _buffer?.IncrementPendingOutboundBytes(messageSize);
        }
예제 #12
0
        /// <summary>
        ///     Removes a pending write operation and release it's message via {@link ReferenceCountUtil#safeRelease(Object)}.
        /// </summary>
        /// <returns><seealso cref="TaskCompletionSource" /> of the pending write or <c>null</c> if the queue is empty.</returns>
        public TaskCompletionSource Remove()
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);

            PendingWrite write = this.head;

            if (write == null)
            {
                return(null);
            }
            TaskCompletionSource promise = write.Promise;

            ReferenceCountUtil.SafeRelease(write.Msg);
            this.Recycle(write, true);
            return(promise);
        }
예제 #13
0
        /**
         * Remove a pending write operation and fail it with the given {@link Throwable}. The message will be released via
         * {@link ReferenceCountUtil#safeRelease(Object)}.
         */

        public void RemoveAndFail(Exception cause)
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);
            Contract.Requires(cause != null);

            PendingWrite write = this.head;

            if (write == null)
            {
                return;
            }
            ReferenceCountUtil.SafeRelease(write.Msg);
            TaskCompletionSource promise = write.Promise;

            Util.SafeSetFailure(promise, cause, Logger);
            this.Recycle(write, true);
        }
예제 #14
0
        /**
         * Removes a pending write operation and performs it via
         * {@link ChannelHandlerContext#write(Object, ChannelPromise)}.
         *
         * @return  {@link ChannelFuture} if something was written and {@code null}
         *          if the {@link PendingWriteQueue} is empty.
         */

        public Task RemoveAndWriteAsync()
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);

            PendingWrite write = this.head;

            if (write == null)
            {
                return(null);
            }
            object msg = write.Msg;
            TaskCompletionSource promise = write.Promise;

            this.Recycle(write, true);
            this.ctx.WriteAsync(msg).LinkOutcome(promise);
            return(promise.Task);
        }
예제 #15
0
        /// <summary>
        /// Connection is closing but a write has to be finished first
        /// </summary>
        private Receive ClosingWithPendingWrite(ConnectionInfo info, IActorRef closeCommander, ConnectionClosed closedEvent)
        {
            return(message =>
            {
                switch (message)
                {
                case SuspendReading _: SuspendReading(); return true;

                case ResumeReading _: ResumeReading(); return true;

                case SocketReceived _: DoRead(info, closeCommander); return true;

                case SocketSent _:
                    AcknowledgeSent();
                    if (IsWritePending)
                    {
                        DoWrite(info);
                    }
                    else
                    {
                        HandleClose(info, closeCommander, closedEvent);
                    }
                    return true;

                case UpdatePendingWriteAndThen updatePendingWrite:
                    pendingWrite = updatePendingWrite.RemainingWrite;
                    updatePendingWrite.Work();

                    if (IsWritePending)
                    {
                        DoWrite(info);
                    }
                    else
                    {
                        HandleClose(info, closeCommander, closedEvent);
                    }
                    return true;

                case WriteFileFailed fail: HandleError(info.Handler, fail.Cause); return true;

                case Abort _: HandleClose(info, Sender, Aborted.Instance); return true;

                default: return false;
                }
            });
        }
예제 #16
0
        /// <summary>
        /// Removes all pending write operation and performs them via <see cref="IChannelHandlerContext.WriteAsync(object, IPromise)"/>
        /// </summary>
        /// <returns><see cref="Task"/> if something was written and <c>null</c>
        /// if the <see cref="PendingWriteQueue"/> is empty.</returns>
        public Task RemoveAndWriteAllAsync()
        {
            Debug.Assert(_ctx.Executor.InEventLoop);

            if (IsEmpty)
            {
                return(null);
            }

            var             p        = _ctx.NewPromise();
            PromiseCombiner combiner = new PromiseCombiner(_ctx.Executor);

            try
            {
                // It is possible for some of the written promises to trigger more writes. The new writes
                // will "revive" the queue, so we need to write them up until the queue is empty.
                for (PendingWrite write = _head; write is object; write = _head)
                {
                    _head  = _tail = null;
                    _size  = 0;
                    _bytes = 0;

                    while (write is object)
                    {
                        PendingWrite next    = write.Next;
                        object       msg     = write.Msg;
                        IPromise     promise = write.Promise;
                        Recycle(write, false);
                        if (!promise.IsVoid)
                        {
                            combiner.Add(promise.Task);
                        }
                        _     = _ctx.WriteAsync(msg, promise);
                        write = next;
                    }
                }
                combiner.Finish(p);
            }
            catch (Exception exc)
            {
                p.SetException(exc);
            }
            AssertEmpty();
            return(p.Task);
        }
예제 #17
0
 private static void HandleEndOfInputFuture(Task task, PendingWrite currentWrite)
 {
     if (task.IsSuccess())
     {
         var chunks = (IChunkedInput <T>)currentWrite.Message;
         // read state of the input in local variables before closing it
         long inputProgress = chunks.Progress;
         long inputLength   = chunks.Length;
         CloseInput(chunks);
         currentWrite.Progress(inputProgress, inputLength);
         currentWrite.Success(inputLength);
     }
     else
     {
         CloseInput((IChunkedInput <T>)currentWrite.Message);
         currentWrite.Fail(task.Exception);
     }
 }
예제 #18
0
        /// <summary>
        /// Remove a pending write operation and fail it with the given <see cref="Exception"/>. The message will be
        /// released via <see cref="ReferenceCountUtil.SafeRelease(object)"/>.
        /// </summary>
        /// <param name="cause">The <see cref="Exception"/> to fail with.</param>
        public void RemoveAndFail(Exception cause)
        {
            Debug.Assert(_ctx.Executor.InEventLoop);
            if (cause is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.cause);
            }

            PendingWrite write = _head;

            if (write is null)
            {
                return;
            }
            ReferenceCountUtil.SafeRelease(write.Msg);
            IPromise promise = write.Promise;

            Util.SafeSetFailure(promise, cause, Logger);
            Recycle(write, true);
        }
예제 #19
0
        /**
         * Remove all pending write operation and fail them with the given {@link Throwable}. The message will be released
         * via {@link ReferenceCountUtil#safeRelease(Object)}.
         */

        public void RemoveAndFailAll(Exception cause)
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);
            Contract.Requires(cause != null);

            // Guard against re-entrance by directly reset
            PendingWrite write = this.head;

            this.head = this.tail = null;
            this.size = 0;

            while (write != null)
            {
                PendingWrite next = write.Next;
                ReferenceCountUtil.SafeRelease(write.Msg);
                TaskCompletionSource promise = write.Promise;
                this.Recycle(write, false);
                Util.SafeSetFailure(promise, cause, Logger);
                write = next;
            }
            this.AssertEmpty();
        }
예제 #20
0
        /// <summary>
        /// Removes all pending write operation and performs them via <see cref="IChannelHandlerContext.WriteAsync(object, IPromise)"/>
        /// </summary>
        /// <returns>An await-able task.</returns>
        public Task RemoveAndWriteAllAsync()
        {
            Debug.Assert(_ctx.Executor.InEventLoop);

            if (IsEmpty)
            {
                return(TaskUtil.Completed);
            }

            // Guard against re-entrance by directly reset
            int currentSize = _size;
            var tasks       = new List <Task>(currentSize);

            // It is possible for some of the written promises to trigger more writes. The new writes
            // will "revive" the queue, so we need to write them up until the queue is empty.
            for (PendingWrite write = _head; write is object; write = _head)
            {
                _head  = _tail = null;
                _size  = 0;
                _bytes = 0;

                while (write is object)
                {
                    PendingWrite next    = write.Next;
                    object       msg     = write.Msg;
                    IPromise     promise = write.Promise;
                    Recycle(write, false);
                    if (!promise.IsVoid)
                    {
                        tasks.Add(promise.Task);
                    }
                    _     = _ctx.WriteAsync(msg, promise);
                    write = next;
                }
            }
            AssertEmpty();
            return(Task.WhenAll(tasks));
        }
예제 #21
0
        /**
         * Remove all pending write operation and performs them via
         * {@link ChannelHandlerContext#write(Object, ChannelPromise)}.
         *
         * @return  {@link ChannelFuture} if something was written and {@code null}
         *          if the {@link PendingWriteQueue} is empty.
         */

        public Task RemoveAndWriteAllAsync()
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);

            if (this.size == 1)
            {
                // No need to use ChannelPromiseAggregator for this case.
                return(this.RemoveAndWriteAsync());
            }
            PendingWrite write = this.head;

            if (write == null)
            {
                // empty so just return null
                return(null);
            }

            // Guard against re-entrance by directly reset
            this.head = this.tail = null;
            int currentSize = this.size;

            this.size = 0;

            var tasks = new List <Task>(currentSize);

            while (write != null)
            {
                PendingWrite         next    = write.Next;
                object               msg     = write.Msg;
                TaskCompletionSource promise = write.Promise;
                this.Recycle(write, false);
                this.ctx.WriteAsync(msg).LinkOutcome(promise);
                tasks.Add(promise.Task);
                write = next;
            }
            this.AssertEmpty();
            return(Task.WhenAll(tasks));
        }
예제 #22
0
        /**
         * Add the given {@code msg} and {@link ChannelPromise}.
         */

        public Task Add(object msg)
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);
            Contract.Requires(msg != null);

            int messageSize = this.estimatorHandle.Size(msg);
            if (messageSize < 0)
            {
                // Size may be unknow so just use 0
                messageSize = 0;
            }
            var promise = new TaskCompletionSource();
            PendingWrite write = PendingWrite.NewInstance(msg, messageSize, promise);
            PendingWrite currentTail = this.tail;
            if (currentTail == null)
            {
                this.tail = this.head = write;
            }
            else
            {
                currentTail.Next = write;
                this.tail = write;
            }
            this.size++;
            // We need to guard against null as channel.Unsafe.OutboundBuffer may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            if (this.buffer != null)
            {
                this.buffer.IncrementPendingOutboundBytes(write.Size);
            }
            return promise.Task;
        }
예제 #23
0
		private async Task AppendInternalAsync(PendingWrite mine)
		{
			using (await _asyncLock.LockAsync())
			{
				var completed = new List<PendingWrite>();
				try
				{
					if (mine.Done())
						return;

					await MakeRoomForWrite(mine.Stream.Length, mine.Etag);

					var seq = _options.Status.NextSeqeunce();

					var room = _currentBufferSize - _memTable.MemTable.ApproximateMemoryUsage;

					PendingWrite write;
					while (_pending.TryDequeue(out write))
					{
						var memoryHandle = _memTable.MemTable.Write(write.Stream);

						await _memTable.Log.Write7BitEncodedIntAsync(memoryHandle.Size);

						using (var stream = _memTable.MemTable.Read(memoryHandle))
						{
							await _memTable.Log.CopyFromAsync(stream);
						}

						_memTable.MemTable.Add(seq, ItemType.Value, EtagToSlice(write.Etag), memoryHandle);

						room -= memoryHandle.Size;

						completed.Add(write);

						PendingWrite next;
						if (_pending.TryPeek(out next) == false)
							break;

						if (next.Stream.Length > room) // don't use more than the current mem table allows
							break;
					}

					foreach (var pendingWrite in completed)
					{
						pendingWrite.Result.SetResult(null);
					}
				}
				catch (Exception e)
				{
					foreach (var pendingWrite in completed)
					{
						pendingWrite.Result.SetException(e);
					}
					throw;
				}
				finally
				{
					_writeCompletedEvent.Pulse();
				}
			}
		}
예제 #24
0
        void DoFlush(IChannelHandlerContext context)
        {
            IChannel channel = context.Channel;

            if (!channel.Active)
            {
                this.Discard();
                return;
            }

            bool requiresFlush             = true;
            IByteBufferAllocator allocator = context.Allocator;

            while (channel.IsWritable)
            {
                if (this.currentWrite == null)
                {
                    this.currentWrite = this.queue.Count > 0 ? this.queue.Dequeue() : null;
                }

                if (this.currentWrite == null)
                {
                    break;
                }

                PendingWrite current        = this.currentWrite;
                object       pendingMessage = current.Message;

                var chunks = pendingMessage as IChunkedInput <T>;
                if (chunks != null)
                {
                    bool   endOfInput;
                    bool   suspend;
                    object message = null;

                    try
                    {
                        message    = chunks.ReadChunk(allocator);
                        endOfInput = chunks.IsEndOfInput;
                        if (message == null)
                        {
                            // No need to suspend when reached at the end.
                            suspend = !endOfInput;
                        }
                        else
                        {
                            suspend = false;
                        }
                    }
                    catch (Exception exception)
                    {
                        this.currentWrite = null;

                        if (message != null)
                        {
                            ReferenceCountUtil.Release(message);
                        }

                        current.Fail(exception);
                        CloseInput(chunks);

                        break;
                    }

                    if (suspend)
                    {
                        // ChunkedInput.nextChunk() returned null and it has
                        // not reached at the end of input. Let's wait until
                        // more chunks arrive. Nothing to write or notify.
                        break;
                    }

                    if (message == null)
                    {
                        // If message is null write an empty ByteBuf.
                        // See https://github.com/netty/netty/issues/1671
                        message = Unpooled.Empty;
                    }

                    Task future = context.WriteAsync(message);
                    if (endOfInput)
                    {
                        this.currentWrite = null;

                        // Register a listener which will close the input once the write is complete.
                        // This is needed because the Chunk may have some resource bound that can not
                        // be closed before its not written.
                        //
                        // See https://github.com/netty/netty/issues/303
                        future.ContinueWith((_, state) =>
                        {
                            var pendingTask = (PendingWrite)state;
                            CloseInput((IChunkedInput <T>)pendingTask.Message);
                            pendingTask.Success();
                        },
                                            current,
                                            TaskContinuationOptions.ExecuteSynchronously);
                    }
                    else if (channel.IsWritable)
                    {
                        future.ContinueWith((task, state) =>
                        {
                            var pendingTask = (PendingWrite)state;
                            if (task.IsFaulted)
                            {
                                CloseInput((IChunkedInput <T>)pendingTask.Message);
                                pendingTask.Fail(task.Exception);
                            }
                            else
                            {
                                pendingTask.Progress(chunks.Progress, chunks.Length);
                            }
                        },
                                            current,
                                            TaskContinuationOptions.ExecuteSynchronously);
                    }
                    else
                    {
                        future.ContinueWith((task, state) =>
                        {
                            var handler = (ChunkedWriteHandler <T>)state;
                            if (task.IsFaulted)
                            {
                                CloseInput((IChunkedInput <T>)handler.currentWrite.Message);
                                handler.currentWrite.Fail(task.Exception);
                            }
                            else
                            {
                                handler.currentWrite.Progress(chunks.Progress, chunks.Length);
                                if (channel.IsWritable)
                                {
                                    handler.ResumeTransfer();
                                }
                            }
                        },
                                            this,
                                            TaskContinuationOptions.ExecuteSynchronously);
                    }

                    // Flush each chunk to conserve memory
                    context.Flush();
                    requiresFlush = false;
                }
                else
                {
                    context.WriteAsync(pendingMessage)
                    .ContinueWith((task, state) =>
                    {
                        var pendingTask = (PendingWrite)state;
                        if (task.IsFaulted)
                        {
                            pendingTask.Fail(task.Exception);
                        }
                        else
                        {
                            pendingTask.Success();
                        }
                    },
                                  current,
                                  TaskContinuationOptions.ExecuteSynchronously);

                    this.currentWrite = null;
                    requiresFlush     = true;
                }

                if (!channel.Active)
                {
                    this.Discard(new ClosedChannelException());
                    break;
                }
            }

            if (requiresFlush)
            {
                context.Flush();
            }
        }
예제 #25
0
 private static void HandleFuture(Task task, ChunkedWriteHandler <T> owner, IChannel channel, PendingWrite currentWrite, bool resume)
 {
     if (task.IsSuccess())
     {
         var chunks = (IChunkedInput <T>)currentWrite.Message;
         currentWrite.Progress(chunks.Progress, chunks.Length);
         if (resume && channel.IsWritable)
         {
             owner.ResumeTransfer();
         }
     }
     else
     {
         CloseInput((IChunkedInput <T>)currentWrite.Message);
         currentWrite.Fail(task.Exception);
     }
 }
예제 #26
0
        private Receive HandleWriteMessages(ConnectionInfo info)
        {
            return(message =>
            {
                switch (message)
                {
                case SocketSent _:
                    AcknowledgeSent();
                    if (IsWritePending)
                    {
                        DoWrite(info);
                        if (!IsWritePending && interestedInResume != null)
                        {
                            interestedInResume.Tell(WritingResumed.Instance);
                            interestedInResume = null;
                        }
                    }
                    return true;

                case WriteCommand write:
                    if (HasStatus(ConnectionStatus.WritingSuspended))
                    {
                        if (traceLogging)
                        {
                            Log.Debug("Dropping write because writing is suspended");
                        }
                        Sender.Tell(write.FailureMessage);
                    }
                    else if (IsWritePending)
                    {
                        if (traceLogging)
                        {
                            Log.Debug("Dropping write because queue is full");
                        }
                        Sender.Tell(write.FailureMessage);
                        if (info.UseResumeWriting)
                        {
                            SetStatus(ConnectionStatus.WritingSuspended);
                        }
                    }
                    else
                    {
                        pendingWrite = CreatePendingWrite(Sender, write, info);
                        if (IsWritePending)
                        {
                            DoWrite(info);
                        }
                    }
                    return true;

                case ResumeWriting _:
                    /*
                     * If more than one actor sends Writes then the first to send this
                     * message might resume too early for the second, leading to a Write of
                     * the second to go through although it has not been resumed yet; there
                     * is nothing we can do about this apart from all actors needing to
                     * register themselves and us keeping track of them, which sounds bad.
                     *
                     * Thus it is documented that useResumeWriting is incompatible with
                     * multiple writers. But we fail as gracefully as we can.
                     */
                    ClearStatus(ConnectionStatus.WritingSuspended);
                    if (IsWritePending)
                    {
                        if (interestedInResume == null)
                        {
                            interestedInResume = Sender;
                        }
                        else
                        {
                            Sender.Tell(new CommandFailed(ResumeWriting.Instance));
                        }
                    }
                    else
                    {
                        Sender.Tell(WritingResumed.Instance);
                    }
                    return true;

                case UpdatePendingWriteAndThen updatePendingWrite:
                    pendingWrite = updatePendingWrite.RemainingWrite;
                    updatePendingWrite.Work();
                    if (IsWritePending)
                    {
                        DoWrite(info);
                    }
                    return true;

                case WriteFileFailed fail:
                    HandleError(info.Handler, fail.Cause);
                    return true;

                default: return false;
                }
            });
        }
예제 #27
0
 private Receive HandleWriteMessages(ConnectionInfo info)
 {
     return(message =>
     {
         if (message is SelectionHandler.ChannelWritable)
         {
             if (WritePending())
             {
                 DoWrite(info);
                 if (!WritePending() && _interestedInResume != null)
                 {
                     _interestedInResume.Tell(IO.Tcp.WritingResumed.Instance);
                     _interestedInResume = null;
                 }
             }
             return true;
         }
         var write = message as Tcp.WriteCommand;
         if (write != null)
         {
             if (_writingSuspended)
             {
                 if (_tcp.Settings.TraceLogging)
                 {
                     _log.Debug("Dropping write because writing is suspended");
                 }
                 Sender.Tell(write.FailureMessage);
             }
             else if (WritePending())
             {
                 if (_tcp.Settings.TraceLogging)
                 {
                     _log.Debug("Dropping write because queue is full");
                 }
                 Sender.Tell(write.FailureMessage);
                 if (info.UseResumeWriting)
                 {
                     _writingSuspended = true;
                 }
             }
             else
             {
                 _pendingWrite = CreatePendingWrite(Sender, write);
                 if (WritePending())
                 {
                     DoWrite(info);
                 }
             }
             return true;
         }
         if (message is Tcp.ResumeWriting)
         {
             /*
              * If more than one actor sends Writes then the first to send this
              * message might resume too early for the second, leading to a Write of
              * the second to go through although it has not been resumed yet; there
              * is nothing we can do about this apart from all actors needing to
              * register themselves and us keeping track of them, which sounds bad.
              *
              * Thus it is documented that useResumeWriting is incompatible with
              * multiple writers. But we fail as gracefully as we can.
              */
             _writingSuspended = false;
             if (WritePending())
             {
                 if (_interestedInResume == null)
                 {
                     _interestedInResume = Sender;
                 }
                 else
                 {
                     Sender.Tell(new Tcp.CommandFailed(IO.Tcp.ResumeWriting.Instance));
                 }
             }
             else
             {
                 Sender.Tell(IO.Tcp.WritingResumed.Instance);
             }
             return true;
         }
         var updatePendingWrite = message as UpdatePendingWriteAndThen;
         if (updatePendingWrite != null)
         {
             _pendingWrite = updatePendingWrite.RemainingWrite;
             updatePendingWrite.Work();
             if (WritePending())
             {
                 info.Registration.EnableInterest(SocketAsyncOperation.Send);
             }
             return true;
         }
         //TODO: File IO
         return false;
     });
 }
예제 #28
0
 public UpdatePendingWriteAndThen(PendingWrite remainingWrite, Action work)
 {
     RemainingWrite = remainingWrite;
     Work           = work;
 }
예제 #29
0
 private void DoWrite(ConnectionInfo info)
 {
     _pendingWrite = _pendingWrite.DoWrite(info);
 }
예제 #30
0
		public async Task AppendAsync(RavenJObject data)
		{
			if (disposed)
				throw new ObjectDisposedException("EventStream");
			var nextEtag = _options.Status.NextEtag();
			data["@etag"] = nextEtag.ToString();
			data["@date"] = DateTime.UtcNow.ToString("o");

			using (var stream = new BufferPoolMemoryStream(_options.BufferPool))
			{
				var bsonWriter = new BsonWriter(stream);
				data.WriteTo(bsonWriter);
				bsonWriter.Flush();
				stream.Position = 0;
				var mine = new PendingWrite(stream, nextEtag);

				_pending.Enqueue(mine);

				while (mine.Done() == false && _pending.Peek() != mine)
				{
					await _writeCompletedEvent.WaitAsync();
				}

				if (mine.Done())
					return;

				await AppendInternalAsync(mine);
			}
		}
예제 #31
0
        /**
         * Remove all pending write operation and fail them with the given {@link Throwable}. The message will be released
         * via {@link ReferenceCountUtil#safeRelease(Object)}.
         */

        public void RemoveAndFailAll(Exception cause)
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);
            Contract.Requires(cause != null);

            // Guard against re-entrance by directly reset
            PendingWrite write = this.head;
            this.head = this.tail = null;
            this.size = 0;

            while (write != null)
            {
                PendingWrite next = write.Next;
                ReferenceCountUtil.SafeRelease(write.Msg);
                TaskCompletionSource promise = write.Promise;
                this.Recycle(write, false);
                SafeFail(promise, cause);
                write = next;
            }
            this.AssertEmpty();
        }
예제 #32
0
        void DoFlush(IChannelHandlerContext context)
        {
            IChannel channel = context.Channel;

            if (!channel.Active)
            {
                Discard();
                return;
            }

            bool requiresFlush             = true;
            IByteBufferAllocator allocator = context.Allocator;

            while (channel.IsWritable)
            {
                PendingWrite currentWrite = _queue.FirstOrDefault;
                if (currentWrite is null)
                {
                    break;
                }

                if (currentWrite.Promise.IsCompleted)
                {
                    // This might happen e.g. in the case when a write operation
                    // failed, but there're still unconsumed chunks left.
                    // Most chunked input sources would stop generating chunks
                    // and report end of input, but this doesn't work with any
                    // source wrapped in HttpChunkedInput.
                    // Note, that we're not trying to release the message/chunks
                    // as this had to be done already by someone who resolved the
                    // promise (using ChunkedInput.close method).
                    // See https://github.com/netty/netty/issues/8700.
                    _ = _queue.RemoveFromFront();
                    continue;
                }

                object pendingMessage = currentWrite.Message;

                if (pendingMessage is IChunkedInput <T> chunks)
                {
                    bool   endOfInput;
                    bool   suspend;
                    object message = null;

                    try
                    {
                        message    = chunks.ReadChunk(allocator);
                        endOfInput = chunks.IsEndOfInput;
                        if (message is null)
                        {
                            // No need to suspend when reached at the end.
                            suspend = !endOfInput;
                        }
                        else
                        {
                            suspend = false;
                        }
                    }
                    catch (Exception exception)
                    {
                        _ = _queue.RemoveFromFront();

                        if (message is object)
                        {
                            _ = ReferenceCountUtil.Release(message);
                        }

                        CloseInput(chunks);
                        currentWrite.Fail(exception);

                        break;
                    }

                    if (suspend)
                    {
                        // ChunkedInput.nextChunk() returned null and it has
                        // not reached at the end of input. Let's wait until
                        // more chunks arrive. Nothing to write or notify.
                        break;
                    }

                    if (message is null)
                    {
                        // If message is null write an empty ByteBuf.
                        // See https://github.com/netty/netty/issues/1671
                        message = Unpooled.Empty;
                    }

                    // Flush each chunk to conserve memory
                    Task future = context.WriteAndFlushAsync(message);
                    if (endOfInput)
                    {
                        _ = _queue.RemoveFromFront();

                        if (future.IsCompleted)
                        {
                            HandleEndOfInputFuture(future, currentWrite);
                        }
                        else
                        {
                            // Register a listener which will close the input once the write is complete.
                            // This is needed because the Chunk may have some resource bound that can not
                            // be closed before its not written.
                            //
                            // See https://github.com/netty/netty/issues/303
                            _ = future.ContinueWith(LinkOutcomeWhenIsEndOfChunkedInputAction, currentWrite, TaskContinuationOptions.ExecuteSynchronously);
                        }
                    }
                    else
                    {
                        var resume = !channel.IsWritable;
                        if (future.IsCompleted)
                        {
                            HandleFuture(future, this, channel, currentWrite, resume);
                        }
                        else
                        {
                            _ = future.ContinueWith(LinkOutcomeAction,
                                                    Tuple.Create(this, channel, currentWrite, resume), TaskContinuationOptions.ExecuteSynchronously);
                        }
                    }

                    requiresFlush = false;
                }
                else
                {
                    _             = _queue.RemoveFromFront();
                    _             = context.WriteAsync(pendingMessage, currentWrite.Promise);
                    requiresFlush = true;
                }

                if (!channel.Active)
                {
                    Discard(new ClosedChannelException());
                    break;
                }
            }

            if (requiresFlush)
            {
                _ = context.Flush();
            }
        }
예제 #33
0
 public void Recycle()
 {
     this.Size = 0;
     this.Next = null;
     this.Msg = null;
     this.Promise = null;
     this.handle.Release(this);
 }
예제 #34
0
        void Recycle(PendingWrite write, bool update)
        {
            PendingWrite next = write.Next;
            long writeSize = write.Size;

            if (update)
            {
                if (next == null)
                {
                    // Handled last PendingWrite so rest head and tail
                    // Guard against re-entrance by directly reset
                    this.head = this.tail = null;
                    this.size = 0;
                }
                else
                {
                    this.head = next;
                    this.size--;
                    Contract.Assert(this.size > 0);
                }
            }

            write.Recycle();
            // We need to guard against null as channel.unsafe().outboundBuffer() may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            if (this.buffer != null)
            {
                this.buffer.DecrementPendingOutboundBytes(writeSize);
            }
        }
예제 #35
0
        /**
         * Remove all pending write operation and performs them via
         * {@link ChannelHandlerContext#write(Object, ChannelPromise)}.
         *
         * @return  {@link ChannelFuture} if something was written and {@code null}
         *          if the {@link PendingWriteQueue} is empty.
         */

        public Task RemoveAndWriteAll()
        {
            Contract.Assert(this.ctx.Executor.InEventLoop);

            if (this.size == 1)
            {
                // No need to use ChannelPromiseAggregator for this case.
                return this.RemoveAndWrite();
            }
            PendingWrite write = this.head;
            if (write == null)
            {
                // empty so just return null
                return null;
            }

            // Guard against re-entrance by directly reset
            this.head = this.tail = null;
            int currentSize = this.size;
            this.size = 0;

            var tasks = new List<Task>(currentSize);
            while (write != null)
            {
                PendingWrite next = write.Next;
                object msg = write.Msg;
                TaskCompletionSource promise = write.Promise;
                this.Recycle(write, false);
                this.ctx.WriteAsync(msg).LinkOutcome(promise);
                tasks.Add(promise.Task);
                write = next;
            }
            this.AssertEmpty();
            return Task.WhenAll(tasks);
        }