using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; namespace KcpSharp { partial class KcpConversation { #if NET6_0_OR_GREATER [ThreadStatic] private static KcpConversation? s_currentObject; private object? _flushStateMachine; struct KcpFlushAsyncMethodBuilder { private readonly KcpConversation _conversation; private StateMachineBox? _task; private static readonly StateMachineBox s_syncSuccessSentinel = new SyncSuccessSentinelStateMachineBox(); public KcpFlushAsyncMethodBuilder(KcpConversation conversation) { _conversation = conversation; _task = null; } public static KcpFlushAsyncMethodBuilder Create() { KcpConversation? conversation = s_currentObject; Debug.Assert(conversation is not null); s_currentObject = null; return new KcpFlushAsyncMethodBuilder(conversation); } #pragma warning disable CA1822 // Mark members as static [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine #pragma warning restore CA1822 // Mark members as static { Debug.Assert(stateMachine is not null); stateMachine.MoveNext(); } public ValueTask Task { get { if (ReferenceEquals(_task, s_syncSuccessSentinel)) { return default; } StateMachineBox stateMachineBox = _task ??= CreateWeaklyTypedStateMachineBox(); return new ValueTask(stateMachineBox, stateMachineBox.Version); } } #pragma warning disable CA1822 // Mark members as static public void SetStateMachine(IAsyncStateMachine stateMachine) #pragma warning restore CA1822 // Mark members as static { Debug.Fail("SetStateMachine should not be used."); } public void SetResult() { if (_task == null) { _task = s_syncSuccessSentinel; } else { _task.SetResult(); } } public void SetException(Exception exception) { SetException(exception, ref _task); } private static void SetException(Exception exception, ref StateMachineBox? boxFieldRef) { if (exception == null) { throw new ArgumentNullException(nameof(exception)); } (boxFieldRef ??= CreateWeaklyTypedStateMachineBox()).SetException(exception); } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { AwaitOnCompleted(ref awaiter, ref stateMachine, ref _task, _conversation); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref _task, _conversation); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? boxRef, KcpConversation conversation) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { StateMachineBox stateMachineBox = GetStateMachineBox(ref stateMachine, ref boxRef, conversation); AwaitUnsafeOnCompleted(ref awaiter, stateMachineBox); } private static void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? box, KcpConversation conversation) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { try { awaiter.OnCompleted(GetStateMachineBox(ref stateMachine, ref box, conversation).MoveNextAction); } catch (Exception exception) { var edi = ExceptionDispatchInfo.Capture(exception); ThreadPool.QueueUserWorkItem(static state => ((ExceptionDispatchInfo)state!).Throw(), edi); } } private static void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, StateMachineBox box) where TAwaiter : ICriticalNotifyCompletion { try { awaiter.UnsafeOnCompleted(box.MoveNextAction); } catch (Exception exception) { var edi = ExceptionDispatchInfo.Capture(exception); ThreadPool.QueueUserWorkItem(static state => ((ExceptionDispatchInfo)state!).Throw(), edi); } } private static StateMachineBox CreateWeaklyTypedStateMachineBox() { return new StateMachineBox(null); } private static StateMachineBox GetStateMachineBox(ref TStateMachine stateMachine, ref StateMachineBox? boxFieldRef, KcpConversation conversation) where TStateMachine : IAsyncStateMachine { StateMachineBox? stateMachineBox = boxFieldRef as StateMachineBox; if (stateMachineBox != null) { return stateMachineBox; } StateMachineBox? stateMachineBox2 = boxFieldRef as StateMachineBox; if (stateMachineBox2 != null) { if (stateMachineBox2.StateMachine == null) { Debugger.NotifyOfCrossThreadDependency(); stateMachineBox2.StateMachine = stateMachine; } return stateMachineBox2; } Debugger.NotifyOfCrossThreadDependency(); StateMachineBox stateMachineBox3 = (StateMachineBox)(boxFieldRef = StateMachineBox.GetOrCreateBox(conversation)); stateMachineBox3.StateMachine = stateMachine; return stateMachineBox3; } abstract class StateMachineBox : IValueTaskSource { protected ManualResetValueTaskSourceCore _mrvtsc; protected Action? _moveNextAction; public virtual Action MoveNextAction => _moveNextAction!; public short Version => _mrvtsc.Version; public void SetResult() { _mrvtsc.SetResult(true); } public void SetException(Exception error) { _mrvtsc.SetException(error); } public ValueTaskSourceStatus GetStatus(short token) { return _mrvtsc.GetStatus(token); } public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _mrvtsc.OnCompleted(continuation, state, token, flags); } void IValueTaskSource.GetResult(short token) { throw new NotSupportedException(); } } sealed class SyncSuccessSentinelStateMachineBox : StateMachineBox { public SyncSuccessSentinelStateMachineBox() { SetResult(); } } sealed class StateMachineBox : StateMachineBox, IValueTaskSource where TStateMachine : IAsyncStateMachine { [MaybeNull] public TStateMachine StateMachine; private KcpConversation? _conversation; public override Action MoveNextAction => _moveNextAction ??= MoveNext; internal StateMachineBox(KcpConversation? conversation) { _conversation = conversation; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StateMachineBox GetOrCreateBox(KcpConversation conversation) { if (conversation._flushStateMachine is StateMachineBox stateMachine) { stateMachine._conversation = conversation; conversation._flushStateMachine = null; return stateMachine; } return new StateMachineBox(conversation); } void IValueTaskSource.GetResult(short token) { try { _mrvtsc.GetResult(token); } finally { ReturnOrDropBox(); } } public void MoveNext() { if (StateMachine is not null) { StateMachine.MoveNext(); } } private void ReturnOrDropBox() { StateMachine = default!; _mrvtsc.Reset(); if (_conversation is not null) { _conversation._flushStateMachine = this; _conversation = null; } } } } #endif } }