// Copyright Epic Games, Inc. All Rights Reserved. using System.Threading; using System.Threading.Tasks; namespace EpicGames.Core { /// /// Wrapper to allow awaiting an event being signalled, similar to an AutoResetEvent. /// public class AsyncEvent { /// /// Completion source to wait on /// TaskCompletionSource _source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); /// /// Legacy name for . /// public void Set() => Pulse(); /// /// Pulse the event, allowing any captured copies of the task to continue. /// public void Pulse() { for (; ; ) { TaskCompletionSource prevSource = _source; if (prevSource.Task.IsCompleted) { // The task source is latched or has been set from another thread. Either way behaves identically wrt the contract of this method. break; } if (Interlocked.CompareExchange(ref _source, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously), prevSource) == prevSource) { prevSource.TrySetResult(true); break; } } } /// /// Resets an event which is latched to the set state. /// public void Reset() { for (; ; ) { TaskCompletionSource prevSource = _source; if (!prevSource.Task.IsCompleted) { break; } else if (Interlocked.CompareExchange(ref _source, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously), prevSource) == prevSource) { break; } } } /// /// Move the task to completed without returning it to an unset state. A call to Reset() is required to clear it. /// public void Latch() { _source.TrySetResult(true); } /// /// Determines if this event is set /// /// True if the event is set public bool IsSet() { return _source.Task.IsCompleted; } /// /// Waits for this event to be set /// public Task Task => _source.Task; } }