private async Task <ChannelReader <object> > StreamAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken) { if (!_startCalled) { throw new InvalidOperationException($"The '{nameof(StreamAsync)}' method cannot be called before the connection has been started."); } var invokeCts = new CancellationTokenSource(); var irq = InvocationRequest.Stream(invokeCts.Token, returnType, GetNextId(), _loggerFactory, this, out var channel); // After InvokeCore we don't want the irq cancellation token to be triggered. // The stream invocation will be canceled by the CancelInvocationMessage, connection closing, or channel finishing. using (cancellationToken.Register(token => ((CancellationTokenSource)token).Cancel(), invokeCts)) { await InvokeStreamCore(methodName, irq, args); } if (cancellationToken.CanBeCanceled) { cancellationToken.Register(state => { var invocationReq = (InvocationRequest)state; if (!invocationReq.HubConnection._connectionActive.IsCancellationRequested) { // Fire and forget, if it fails that means we aren't connected anymore. _ = invocationReq.HubConnection.SendHubMessage(new CancelInvocationMessage(invocationReq.InvocationId), invocationReq); if (invocationReq.HubConnection.TryRemoveInvocation(invocationReq.InvocationId, out _)) { invocationReq.Complete(CompletionMessage.Empty(irq.InvocationId)); } invocationReq.Dispose(); } }, irq); } return(channel); }
private async Task <object> InvokeCoreAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken) { CheckDisposed(); await WaitConnectionLockAsync(); Task <object> invocationTask; try { CheckDisposed(); CheckConnectionActive(nameof(InvokeCoreAsync)); var irq = InvocationRequest.Invoke(cancellationToken, returnType, _connectionState.GetNextId(), _loggerFactory, this, out invocationTask); await InvokeCore(methodName, irq, args, cancellationToken); } finally { ReleaseConnectionLock(); } // Wait for this outside the lock, because it won't complete until the server responds. return(await invocationTask); }
// This async void is GROSS but we need to dispatch asynchronously because we're writing to a Channel // and there's nobody to actually wait for us to finish. private async void DispatchInvocationStreamItemAsync(StreamItemMessage streamItem, InvocationRequest irq) { _logger.ReceivedStreamItem(streamItem.InvocationId); if (irq.CancellationToken.IsCancellationRequested) { _logger.CancelingStreamItem(irq.InvocationId); } else if (!await irq.StreamItem(streamItem.Item)) { _logger.ReceivedStreamItemAfterClose(irq.InvocationId); } }
private Task SendAsyncCore(string methodName, object[] args, CancellationToken cancellationToken) { var irq = InvocationRequest.Invoke(cancellationToken, typeof(void), GetNextId(), _loggerFactory, out _); return(InvokeCore(methodName, irq, args, nonBlocking: true)); }