/// <summary> /// This writes a message to the output stream /// </summary> /// <param name="physical">The phsyical connection to write to.</param> /// <param name="message">The message to be written.</param> internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message) { Trace("Writing: " + message); message.SetEnqueued(physical); // this also records the read/write stats at this point bool releaseLock = false; MutexSlim.LockToken token = default; try { // try to acquire it synchronously // note: timeout is specified in mutex-constructor var pending = _singleWriterMutex.TryWaitAsync(options: MutexSlim.WaitOptions.DisableAsyncContext); if (!pending.IsCompletedSuccessfully) { return(WriteMessageTakingDelayedWriteLockAsync(pending, physical, message)); } releaseLock = true; token = pending.GetResult(); // we can't use "using" for this, because we might not want to kill it yet if (!token.Success) // (in particular, me might hand the lifetime to CompleteWriteAndReleaseLockAsync) { message.Cancel(); Multiplexer?.OnMessageFaulted(message, null); this.CompleteSyncOrAsync(message); return(new ValueTask <WriteResult>(WriteResult.TimeoutBeforeWrite)); } var result = WriteMessageInsideLock(physical, message); if (result == WriteResult.Success) { var flush = physical.FlushAsync(false); if (!flush.IsCompletedSuccessfully) { releaseLock = false; // so we don't release prematurely return(CompleteWriteAndReleaseLockAsync(token, flush, message)); } result = flush.Result; // we know it was completed, this is fine } UnmarkActiveMessage(message); physical.SetIdle(); return(new ValueTask <WriteResult>(result)); } catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); } finally { if (releaseLock) { token.Dispose(); } } }
private async ValueTask <WriteResult> CompleteWriteAndReleaseLockAsync(MutexSlim.LockToken lockToken, ValueTask <WriteResult> flush, Message message) { using (lockToken) { try { var result = await flush.ForAwait(); UnmarkActiveMessage(message); physical.SetIdle(); return(result); } catch (Exception ex) { return(HandleWriteException(message, ex)); } } }