Exemple #1
0
        private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent connectionEvent)
        {
            // This is the final event on the connection, so free the GCHandle used by the event callback.
            state.StateGCHandle.Free();

            state.Connection = null;

            state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success);

            // Stop accepting new streams.
            state.AcceptQueue.Writer.TryComplete();

            // Stop notifying about available streams.
            TaskCompletionSource?unidirectionalTcs = null;
            TaskCompletionSource?bidirectionalTcs  = null;

            lock (state)
            {
                unidirectionalTcs = state.NewUnidirectionalStreamsAvailable;
                bidirectionalTcs  = state.NewBidirectionalStreamsAvailable;
                state.NewUnidirectionalStreamsAvailable = null;
                state.NewBidirectionalStreamsAvailable  = null;
            }

            if (unidirectionalTcs is not null)
            {
                unidirectionalTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException()));
            }
            if (bidirectionalTcs is not null)
            {
                bidirectionalTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException()));
            }
            return(MsQuicStatusCodes.Success);
        }
Exemple #2
0
        private static uint HandleEventStreamsAvailable(State state, ref ConnectionEvent connectionEvent)
        {
            TaskCompletionSource?unidirectionalTcs = null;
            TaskCompletionSource?bidirectionalTcs  = null;

            lock (state)
            {
                if (connectionEvent.Data.StreamsAvailable.UniDirectionalCount > 0)
                {
                    unidirectionalTcs = state.NewUnidirectionalStreamsAvailable;
                    state.NewUnidirectionalStreamsAvailable = null;
                }

                if (connectionEvent.Data.StreamsAvailable.BiDirectionalCount > 0)
                {
                    bidirectionalTcs = state.NewBidirectionalStreamsAvailable;
                    state.NewBidirectionalStreamsAvailable = null;
                }
            }

            if (unidirectionalTcs is not null)
            {
                unidirectionalTcs.SetResult();
            }
            if (bidirectionalTcs is not null)
            {
                bidirectionalTcs.SetResult();
            }

            return(MsQuicStatusCodes.Success);
        }
Exemple #3
0
        internal override ValueTask WaitForAvailableBidirectionalStreamsAsync(CancellationToken cancellationToken = default)
        {
            TaskCompletionSource?tcs = _state.NewBidirectionalStreamsAvailable;

            if (tcs is null)
            {
                // We need to avoid calling MsQuic under lock.
                // This is not atomic but it won't be anyway as counts can change between when task is completed
                // and before somebody may try to allocate new stream.
                int count = GetRemoteAvailableBidirectionalStreamCount();
                lock (_state)
                {
                    if (_state.NewBidirectionalStreamsAvailable is null)
                    {
                        if (_state.ShutdownTcs.Task.IsCompleted)
                        {
                            throw new QuicOperationAbortedException();
                        }

                        if (count > 0)
                        {
                            return(ValueTask.CompletedTask);
                        }

                        _state.NewBidirectionalStreamsAvailable = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
                    }
                    tcs = _state.NewBidirectionalStreamsAvailable;
                }
            }

            return(new ValueTask(tcs.Task.WaitAsync(cancellationToken)));
        }
Exemple #4
0
        internal override ValueTask WaitForAvailableBidirectionalStreamsAsync(CancellationToken cancellationToken = default)
        {
            TaskCompletionSource?tcs = _state.NewBidirectionalStreamsAvailable;

            if (tcs is null)
            {
                lock (_state)
                {
                    if (_state.NewBidirectionalStreamsAvailable is null)
                    {
                        if (_state.ShutdownTcs.Task.IsCompleted)
                        {
                            throw new QuicOperationAbortedException();
                        }

                        if (GetRemoteAvailableBidirectionalStreamCount() > 0)
                        {
                            return(ValueTask.CompletedTask);
                        }

                        _state.NewBidirectionalStreamsAvailable = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
                    }
                    tcs = _state.NewBidirectionalStreamsAvailable;
                }
            }

            return(new ValueTask(tcs.Task.WaitAsync(cancellationToken)));
        }
Exemple #5
0
        public static async Task <T> AttachAndRun <T>(this FrameworkElement view, Func <Task <T> > action)
        {
            if (view.Parent is Border wrapper)
            {
                view = wrapper;
            }

            TaskCompletionSource?tcs = null;

            if (view.Parent == null)
            {
                // prepare to wait for element to be in the UI
                tcs          = new TaskCompletionSource();
                view.Loaded += OnViewLoaded;

                // attach to the UI
                Grid grid;
                var  window = new Window
                {
                    Content = new Grid
                    {
                        HorizontalAlignment = HorizontalAlignment.Center,
                        VerticalAlignment   = VerticalAlignment.Center,
                        Children            =
                        {
                            (grid        = new Grid
                            {
                                Width    = view.Width,
                                Height   = view.Height,
                                Children =
                                {
                                    view
                                }
                            })
                        }
                    }
                };
                window.Activate();

                // wait for element to be loaded
                await tcs.Task;

                // continue with the run
                try
                {
                    return(await Run(action));
                }
                finally
                {
                    grid.Children.Clear();
                    window.Close();
                }
            }
            else
            {
                return(await Run(action));
            }
Exemple #6
0
 /// <inheritdoc />
 public async Task Start()
 {
     logger.LogInformation("Starting Timer.");
     cancellationTokenSource = new CancellationTokenSource();
     cancellationToken       = cancellationTokenSource.Token;
     startPromise            = new TaskCompletionSource();
     stopPromise             = new TaskCompletionSource();
     thread = new Thread(RunAsync);
     thread.Start();
     await startPromise.Task;
 }
Exemple #7
0
        public void Initialize(Http3StreamContext context)
        {
            base.Initialize(context);

            InputRemaining = null;

            _context = context;

            _errorCodeFeature   = _context.ConnectionFeatures.Get <IProtocolErrorCodeFeature>() !;
            _streamIdFeature    = _context.ConnectionFeatures.Get <IStreamIdFeature>() !;
            _streamAbortFeature = _context.ConnectionFeatures.Get <IStreamAbortFeature>() !;

            _appCompleted = null;
            _isClosed     = 0;
            _requestHeaderParsingState = default;
            _parsedPseudoHeaderFields  = default;
            _totalParsedHeaderSize     = 0;
            _isMethodConnect           = false;
            _completionState           = default;
            StreamTimeoutTicks         = 0;

            if (_frameWriter == null)
            {
                _frameWriter = new Http3FrameWriter(
                    context.StreamContext,
                    context.TimeoutControl,
                    context.ServiceContext.ServerOptions.Limits.MinResponseDataRate,
                    context.MemoryPool,
                    context.ServiceContext.Log,
                    _streamIdFeature,
                    context.ClientPeerSettings,
                    this);

                _http3Output = new Http3OutputProducer(
                    _frameWriter,
                    context.MemoryPool,
                    this,
                    context.ServiceContext.Log);
                Output          = _http3Output;
                RequestBodyPipe = CreateRequestBodyPipe(64 * 1024); // windowSize?
                QPackDecoder    = new QPackDecoder(_context.ServiceContext.ServerOptions.Limits.Http3.MaxRequestHeaderFieldSize);
            }
            else
            {
                _http3Output.StreamReset();
                RequestBodyPipe.Reset();
                QPackDecoder.Reset();
            }

            _frameWriter.Reset(context.Transport.Output, context.ConnectionId);
        }
Exemple #8
0
        private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent connectionEvent)
        {
            // This is the final event on the connection, so free the GCHandle used by the event callback.
            state.StateGCHandle.Free();

            if (state.ListenerState != null)
            {
                // This is inbound connection that never got connected - becasue of TLS validation or some other reason.
                // Remove connection from pending queue and dispose it.
                if (state.ListenerState.PendingConnections.TryRemove(state.Handle.DangerousGetHandle(), out MsQuicConnection? connection))
                {
                    connection.Dispose();
                }

                state.ListenerState = null;
            }

            state.Connection = null;

            state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success);

            // Stop accepting new streams.
            state.AcceptQueue.Writer.TryComplete();

            // Stop notifying about available streams.
            TaskCompletionSource?unidirectionalTcs = null;
            TaskCompletionSource?bidirectionalTcs  = null;

            lock (state)
            {
                unidirectionalTcs = state.NewUnidirectionalStreamsAvailable;
                bidirectionalTcs  = state.NewBidirectionalStreamsAvailable;
                state.NewUnidirectionalStreamsAvailable = null;
                state.NewBidirectionalStreamsAvailable  = null;
            }

            if (unidirectionalTcs is not null)
            {
                unidirectionalTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException()));
            }
            if (bidirectionalTcs is not null)
            {
                bidirectionalTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException()));
            }

            return(MsQuicStatusCodes.Success);
        }
        private async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            CancellationTokenRegistration cancellationTokenRegistration = default;

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }

                if (!_inOpaqueMode)
                {
                    await _outputStream.WriteAsync(buffer, offset, count, cancellationToken).SuppressContextFlow();
                }
                else
                {
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Debug.Assert(Interlocked.Increment(ref _outstandingOperations._writes) == 1,
                                 "Only one outstanding write allowed at any given time.");
#endif
                    _writeTaskCompletionSource   = new TaskCompletionSource();
                    _writeEventArgs !.BufferList = null;
                    _writeEventArgs.SetBuffer(buffer, offset, count);
                    if (WriteAsyncFast(_writeEventArgs))
                    {
                        await _writeTaskCompletionSource.Task.SuppressContextFlow();
                    }
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();
            }
        }
Exemple #10
0
    public async ValueTask SendAsync(
        object message,
        Action <MessageMetadata>?metadataSetter,
        AcknowledgementType acknowledgementType,
        CancellationToken cancellationToken)
    {
        string?subscriptionId = null;  // For acknowledgement.

        if (!IsConnected)
        {
            throw new InvalidOperationException("Not connected.");
        }

        var metadata = _metadataFactory.CreateFor(message);

        metadataSetter?.Invoke(metadata);
        metadata.AcknowledgementType = acknowledgementType;

        var reconnectedTimes = 0;

        while (reconnectedTimes < _reconnectRetryCount)
        {
            // This creates a deadlock. Come up with another solution.
            // Do not allow starting new operations while reconnect is happening.
            // await WaitForReconnectAsync(cancellationToken).ConfigureAwait(false);

            var resource = GetConnectionResource();

            try
            {
                TaskCompletionSource?tcs = null;

                if (acknowledgementType == AcknowledgementType.Received)
                {
                    // Setup subscription for acknowledgement.

                    tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

                    ValueTask Handler(AcknowledgeReceived acknowledgeReceived)
                    {
                        if (acknowledgeReceived.MessageId == metadata.MessageId)
                        {
                            tcs.SetResult();
                        }

                        return(default);
Exemple #11
0
            public void Decrement()
            {
                TaskCompletionSource?availableTcs = null;

                lock (_syncRoot)
                {
                    --_actualCount;
                    if (!_availableTcs.Task.IsCompleted)
                    {
                        availableTcs  = _availableTcs;
                        _availableTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
                    }
                }
                if (availableTcs is not null)
                {
                    availableTcs.SetResult();
                }
            }
        public async Task CloseNetworkConnectionAsync(CancellationToken cancellationToken)
        {
            // need to yield here to make sure that we don't get any exception synchronously
            await Task.Yield();

            CancellationTokenRegistration cancellationTokenRegistration = default;

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }
#if DEBUG
                // When using fast path only one outstanding read is permitted. By switching into opaque mode
                // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                // caller takes responsibility for enforcing this constraint.
                Debug.Assert(Interlocked.Increment(ref _outstandingOperations._writes) == 1,
                             "Only one outstanding write allowed at any given time.");
#endif
                _writeTaskCompletionSource = new TaskCompletionSource();
                _writeEventArgs !.SetShouldCloseOutput();
                if (WriteAsyncFast(_writeEventArgs))
                {
                    await _writeTaskCompletionSource.Task.SuppressContextFlow();
                }
            }
            catch (Exception error)
            {
                if (!s_CanHandleException(error))
                {
                    throw;
                }

                // throw OperationCancelledException when canceled by the caller
                // otherwise swallow the exception
                cancellationToken.ThrowIfCancellationRequested();
            }
            finally
            {
                cancellationTokenRegistration.Dispose();
            }
        }
        private async Task MultipleWriteAsyncCore(IList <ArraySegment <byte> > sendBuffers, CancellationToken cancellationToken)
        {
            Debug.Assert(sendBuffers != null, "'sendBuffers' MUST NOT be NULL.");
            Debug.Assert(sendBuffers.Count == 2, "'sendBuffers.Count' MUST be '2' at this point.");

            CancellationTokenRegistration cancellationTokenRegistration = default;

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }
#if DEBUG
                // When using fast path only one outstanding read is permitted. By switching into opaque mode
                // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                // caller takes responsibility for enforcing this constraint.
                Debug.Assert(Interlocked.Increment(ref _outstandingOperations._writes) == 1,
                             "Only one outstanding write allowed at any given time.");
#endif
                _writeTaskCompletionSource = new TaskCompletionSource();
                _writeEventArgs !.SetBuffer(null, 0, 0);
                _writeEventArgs.BufferList = sendBuffers;
                if (WriteAsyncFast(_writeEventArgs))
                {
                    await _writeTaskCompletionSource.Task.SuppressContextFlow();
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();
            }
        }
Exemple #14
0
        public async ValueTask <ConnectionContext> AcceptAsync(CancellationToken cancellationToken = default)
        {
            if (_currentConnection != null && _connectionClosed != null)
            {
                await _connectionClosed.Task;
            }

            if (_unbound)
            {
                return(null);
            }

            while (!cancellationToken.IsCancellationRequested &&
                   !_unbound)
            {
                try
                {
                    var newContext = await _client.ConnectToServer(cancellationToken);

                    var connectionClosed = new TaskCompletionSource();

                    newContext.ConnectionClosed.Register(() =>
                    {
                        _logger.LogInformation("Server connection to {0} lost", _client.ServerEndPoint);
                        connectionClosed.SetResult();
                    });

                    _currentConnection = newContext;
                    _connectionClosed  = connectionClosed;

                    _logger.LogInformation("Server connection to {0} established", _client.ServerEndPoint);

                    return(newContext);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error establishing connection to {0}", _client.ServerEndPoint);
                }
            }

            return(null);
        }
 private PendingMessage(ReadOnlyOwnedMemory <byte, IMemoryOwner <byte> > content, TaskCompletionSource?messageSentTaskSource, CancellationToken cancellationToken)
 {
     Content                    = content;
     CancellationToken          = cancellationToken;
     this.messageSentTaskSource = messageSentTaskSource;
 }
Exemple #16
0
 public ToDo(Action?action, TaskCompletionSource?tcs)
 {
     Action   = action;
     Complete = tcs;
     Task     = null;
 }
Exemple #17
0
 public ToDo(Func <Task>?action, TaskCompletionSource?tcs)
 {
     Action   = null;
     Complete = tcs;
     Task     = action;
 }