public async Task DisconnectAsync() { if (_runTask == null) { return; } await TaskUtilities.SwitchToBackgroundThread(); // We may get MessageTransportException from any concurrent SendAsync or ReceiveAsync when the host // drops connection after we request it to do so. To ensure that those don't bubble up to the // client, cancel this token to indicate that we're shutting down the host - SendAsync and // ReceiveAsync will take care of wrapping any WSE into OperationCanceledException. _cts.Cancel(); var token = await _disconnectLock.WaitAsync(); if (!token.IsSet) { try { // Don't use _cts, since it's already cancelled. We want to try to send this message in // any case, and we'll catch MessageTransportException if no-one is on the other end anymore. await _transport.CloseAsync(); token.Set(); } catch (OperationCanceledException) { } catch (MessageTransportException) { } finally { token.Reset(); } } try { if (_runTask.Status == TaskStatus.Running) { await _runTask; } } catch (OperationCanceledException) { // Expected during disconnect. } catch (MessageTransportException) { // Possible and valid during disconnect. } }