internal void Write(int numBytes, IStreamByteDistributorWriter writer)
 {
     Debug.Assert(_stream is object);
     try
     {
         writer.Write(_stream, numBytes);
     }
     catch (Exception t)
     {
         ThrowHelper.ThrowConnectionError_ByteDistributionWriteError(t);
     }
 }
Exemplo n.º 2
0
 /// <summary>
 /// Write any allocated bytes for the given stream and updates the streamable bytes,
 /// assuming all of the bytes will be written.
 /// </summary>
 /// <param name="numBytes"></param>
 /// <param name="writer"></param>
 public void Write(int numBytes, IStreamByteDistributorWriter writer)
 {
     _writing = true;
     try
     {
         // Write the allocated bytes.
         writer.Write(_stream, numBytes);
     }
     catch (Exception t)
     {
         ThrowHelper.ThrowConnectionError_ByteDistributionWriteError(t);
     }
     finally
     {
         _writing = false;
     }
 }
        int Distribute(int maxBytes, IStreamByteDistributorWriter writer, State state)
        {
            if (state.IsActive())
            {
                int nsent = Math.Min(maxBytes, state._streamableBytes);
                state.Write(nsent, writer);
                if (0u >= (uint)nsent && maxBytes != 0)
                {
                    // If a stream sends zero bytes, then we gave it a chance to write empty frames and it is now
                    // considered inactive until the next call to updateStreamableBytes. This allows descendant streams to
                    // be allocated bytes when the parent stream can't utilize them. This may be as a result of the
                    // stream's flow control window being 0.
                    state.UpdateStreamableBytes(state._streamableBytes, false);
                }

                return(nsent);
            }

            return(DistributeToChildren(maxBytes, writer, state));
        }
        /// <summary>
        /// It is a pre-condition that <see cref="State.PollPseudoTimeQueue"/> returns a non-<c>null</c> value. This is a result of the way
        /// the allocation algorithm is structured and can be explained in the following cases:
        /// <h3>For the recursive case</h3>
        /// If a stream has no children (in the allocation tree) than that node must be active or it will not be in the
        /// allocation tree. If a node is active then it will not delegate to children and recursion ends.
        /// <h3>For the initial case</h3>
        /// We check connectionState.activeCountForTree == 0 before any allocation is done. So if the connection stream
        /// has no active children we don't get into this method.
        /// </summary>
        /// <param name="maxBytes"></param>
        /// <param name="writer"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        int DistributeToChildren(int maxBytes, IStreamByteDistributorWriter writer, State state)
        {
            long  oldTotalQueuedWeights = state._totalQueuedWeights;
            State childState            = state.PollPseudoTimeQueue();
            State nextChildState        = state.PeekPseudoTimeQueue();

            childState.SetDistributing();
            try
            {
                Debug.Assert(
                    nextChildState is null || nextChildState._pseudoTimeToWrite >= childState._pseudoTimeToWrite,
                    $"nextChildState[{nextChildState?._streamId}].pseudoTime({nextChildState?._pseudoTimeToWrite}) <  childState[{childState._streamId}].pseudoTime({childState._pseudoTimeToWrite})");

                int nsent = Distribute(
                    nextChildState is null
                        ? maxBytes
                        : Math.Min(
                        maxBytes,
                        (int)Math.Min(
                            (nextChildState._pseudoTimeToWrite - childState._pseudoTimeToWrite) * childState._weight / oldTotalQueuedWeights + _allocationQuantum,
                            int.MaxValue
                            )
                        ),
                    writer,
                    childState);
                state._pseudoTime += nsent;
                childState.UpdatePseudoTime(state, nsent, oldTotalQueuedWeights);
                return(nsent);
            }
            finally
            {
                childState.UnsetDistributing();
                // Do in finally to ensure the internal flags is not corrupted if an exception is thrown.
                // The offer operation is delayed until we unroll up the recursive stack, so we don't have to remove from
                // the priority pseudoTimeQueue due to a write operation.
                if (childState._activeCountForTree != 0)
                {
                    state.OfferPseudoTimeQueue(childState);
                }
            }
        }
Exemplo n.º 5
0
        public bool Distribute(int maxBytes, IStreamByteDistributorWriter writer)
        {
            var size = _queue.Count;

            if (0u >= (uint)size)
            {
                return(_totalStreamableBytes > 0L);
            }

            var chunkSize = Math.Max(_minAllocationChunk, maxBytes / size);

            State state = _queue.RemoveFirst();

            do
            {
                state._enqueued = false;
                if (state._windowNegative)
                {
                    continue;
                }
                if (0u >= (uint)maxBytes && state._streamableBytes > 0)
                {
                    // Stop at the first state that can't send. Add this state back to the head of the queue. Note
                    // that empty frames at the head of the queue will always be written, assuming the stream window
                    // is not negative.
                    _queue.AddFirst​(state);
                    state._enqueued = true;
                    break;
                }

                // Allocate as much data as we can for this stream.
                int chunk = Math.Min(chunkSize, Math.Min(maxBytes, state._streamableBytes));
                maxBytes -= chunk;

                // Write the allocated bytes and enqueue as necessary.
                state.Write(chunk, writer);
            } while (_queue.TryRemoveFirst(out state));

            return(_totalStreamableBytes > 0L);
        }
        public bool Distribute(int maxBytes, IStreamByteDistributorWriter writer)
        {
            // As long as there is some active frame we should write at least 1 time.
            if (0u >= (uint)_connectionState._activeCountForTree)
            {
                return(false);
            }

            // The goal is to write until we write all the allocated bytes or are no longer making progress.
            // We still attempt to write even after the number of allocated bytes has been exhausted to allow empty frames
            // to be sent. Making progress means the active streams rooted at the connection stream has changed.
            int oldIsActiveCountForTree;

            do
            {
                oldIsActiveCountForTree = _connectionState._activeCountForTree;
                // connectionState will never be active, so go right to its children.
                maxBytes -= DistributeToChildren(maxBytes, writer, _connectionState);
            }while (_connectionState._activeCountForTree != 0 &&
                    (maxBytes > 0 || oldIsActiveCountForTree != _connectionState._activeCountForTree));

            return(_connectionState._activeCountForTree != 0);
        }