using System; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; namespace ETModel { internal class ExceptionHolder { private readonly ExceptionDispatchInfo exception; private bool calledGet; public ExceptionHolder(ExceptionDispatchInfo exception) { this.exception = exception; } public ExceptionDispatchInfo GetException() { if (calledGet) { return this.exception; } this.calledGet = true; GC.SuppressFinalize(this); return exception; } } public class ETTaskCompletionSource: IAwaiter { // State(= AwaiterStatus) private const int Pending = 0; private const int Succeeded = 1; private const int Faulted = 2; private const int Canceled = 3; private int state; private ExceptionHolder exception; private Action continuation; // action or list AwaiterStatus IAwaiter.Status => (AwaiterStatus) state; bool IAwaiter.IsCompleted => state != Pending; public ETTask Task => new ETTask(this); void IAwaiter.GetResult() { switch (this.state) { case Succeeded: return; case Faulted: this.exception.GetException().Throw(); return; case Canceled: { if (this.exception != null) { this.exception.GetException().Throw(); // guranteed operation canceled exception. } throw new OperationCanceledException(); } default: throw new NotSupportedException("ETTask does not allow call GetResult directly when task not completed. Please use 'await'."); } } void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) { this.continuation = action; if (state != Pending) { TryInvokeContinuation(); } } private void TryInvokeContinuation() { this.continuation?.Invoke(); this.continuation = null; } public void SetResult() { if (this.TrySetResult()) { return; } throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted"); } public void SetException(Exception e) { if (this.TrySetException(e)) { return; } throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted"); } public bool TrySetResult() { if (this.state != Pending) { return false; } this.state = Succeeded; this.TryInvokeContinuation(); return true; } public bool TrySetException(Exception e) { if (this.state != Pending) { return false; } this.state = Faulted; this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(e)); this.TryInvokeContinuation(); return true; } public bool TrySetCanceled() { if (this.state != Pending) { return false; } this.state = Canceled; this.TryInvokeContinuation(); return true; } public bool TrySetCanceled(OperationCanceledException e) { if (this.state != Pending) { return false; } this.state = Canceled; this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(e)); this.TryInvokeContinuation(); return true; } void INotifyCompletion.OnCompleted(Action action) { ((ICriticalNotifyCompletion) this).UnsafeOnCompleted(action); } } public class ETTaskCompletionSource: IAwaiter { // State(= AwaiterStatus) private const int Pending = 0; private const int Succeeded = 1; private const int Faulted = 2; private const int Canceled = 3; private int state; private T value; private ExceptionHolder exception; private Action continuation; // action or list bool IAwaiter.IsCompleted => state != Pending; public ETTask Task => new ETTask(this); public ETTask UnitTask => new ETTask(this); AwaiterStatus IAwaiter.Status => (AwaiterStatus) state; T IAwaiter.GetResult() { switch (this.state) { case Succeeded: return this.value; case Faulted: this.exception.GetException().Throw(); return default; case Canceled: { if (this.exception != null) { this.exception.GetException().Throw(); // guranteed operation canceled exception. } throw new OperationCanceledException(); } default: throw new NotSupportedException("ETTask does not allow call GetResult directly when task not completed. Please use 'await'."); } } void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) { this.continuation = action; if (state != Pending) { TryInvokeContinuation(); } } private void TryInvokeContinuation() { this.continuation?.Invoke(); this.continuation = null; } public void SetResult(T result) { if (this.TrySetResult(result)) { return; } throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted"); } public void SetException(Exception e) { if (this.TrySetException(e)) { return; } throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted"); } public bool TrySetResult(T result) { if (this.state != Pending) { return false; } this.state = Succeeded; this.value = result; this.TryInvokeContinuation(); return true; } public bool TrySetException(Exception e) { if (this.state != Pending) { return false; } this.state = Faulted; this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(e)); this.TryInvokeContinuation(); return true; } public bool TrySetCanceled() { if (this.state != Pending) { return false; } this.state = Canceled; this.TryInvokeContinuation(); return true; } public bool TrySetCanceled(OperationCanceledException e) { if (this.state != Pending) { return false; } this.state = Canceled; this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(e)); this.TryInvokeContinuation(); return true; } void IAwaiter.GetResult() { ((IAwaiter) this).GetResult(); } void INotifyCompletion.OnCompleted(Action action) { ((ICriticalNotifyCompletion) this).UnsafeOnCompleted(action); } } }