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 (Exception ex) when (ex is OperationCanceledException || ex is MessageTransportException) {
token.Set();
} finally {
token.Reset();
}
}
try {
await _runTask;
} catch (OperationCanceledException) {
// Expected during disconnect.
} catch (MessageTransportException) {
// Possible and valid during disconnect.
}
}