using System.Diagnostics; using System.Threading.Tasks.Sources; namespace NahidaImpact.Kcp { internal sealed class KcpRawSendOperation : IValueTaskSource, IDisposable { private readonly AsyncAutoResetEvent _notification; private ManualResetValueTaskSourceCore _mrvtsc; private bool _transportClosed; private bool _disposed; private bool _activeWait; private bool _signaled; private ReadOnlyMemory _buffer; private CancellationToken _cancellationToken; private CancellationTokenRegistration _cancellationRegistration; public KcpRawSendOperation(AsyncAutoResetEvent notification) { _notification = notification; _mrvtsc = new ManualResetValueTaskSourceCore() { RunContinuationsAsynchronously = true }; } bool IValueTaskSource.GetResult(short token) { _cancellationRegistration.Dispose(); try { return _mrvtsc.GetResult(token); } finally { _mrvtsc.Reset(); lock (this) { _activeWait = false; _signaled = false; _cancellationRegistration = default; } } } ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _mrvtsc.GetStatus(token); void IValueTaskSource.OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _mrvtsc.OnCompleted(continuation, state, token, flags); public ValueTask SendAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { short token; lock (this) { if (_transportClosed || _disposed) { return new ValueTask(false); } if (_activeWait) { return new ValueTask(Task.FromException(ThrowHelper.NewConcurrentSendException())); } if (cancellationToken.IsCancellationRequested) { return new ValueTask(Task.FromCanceled(cancellationToken)); } _activeWait = true; Debug.Assert(!_signaled); _buffer = buffer; _cancellationToken = cancellationToken; token = _mrvtsc.Version; } _cancellationRegistration = cancellationToken.UnsafeRegister(state => ((KcpRawSendOperation)state)!.SetCanceled(), this); _notification.Set(buffer.Length); return new ValueTask(this, token); } public bool CancelPendingOperation(Exception innerException, CancellationToken cancellationToken) { lock (this) { if (_activeWait && !_signaled) { ClearPreviousOperation(); _mrvtsc.SetException(ThrowHelper.NewOperationCanceledExceptionForCancelPendingSend(innerException, cancellationToken)); return true; } } return false; } private void SetCanceled() { lock (this) { if (_activeWait && !_signaled) { CancellationToken cancellationToken = _cancellationToken; ClearPreviousOperation(); _mrvtsc.SetException(new OperationCanceledException(cancellationToken)); } } } private void ClearPreviousOperation() { _signaled = true; _buffer = default; _cancellationToken = default; } public bool TryConsume(Memory buffer, out int bytesWritten) { lock (this) { if (_transportClosed || _disposed) { bytesWritten = 0; return false; } if (!_activeWait) { bytesWritten = 0; return false; } ReadOnlyMemory source = _buffer; if (source.Length > buffer.Length) { ClearPreviousOperation(); _mrvtsc.SetException(ThrowHelper.NewMessageTooLargeForBufferArgument()); bytesWritten = 0; return false; } source.CopyTo(buffer); bytesWritten = source.Length; ClearPreviousOperation(); _mrvtsc.SetResult(true); return true; } } public void SetTransportClosed() { lock (this) { if (_transportClosed || _disposed) { return; } if (_activeWait && !_signaled) { ClearPreviousOperation(); _mrvtsc.SetResult(false); } _transportClosed = true; } } public void Dispose() { lock (this) { if (_disposed) { return; } if (_activeWait && !_signaled) { ClearPreviousOperation(); _mrvtsc.SetResult(false); } _disposed = true; _transportClosed = true; } } } }