mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-17 01:33:03 +08:00
129 lines
3.5 KiB
C#
129 lines
3.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MQTTnet.Internal
|
|
{
|
|
// Inspired from Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-2-asyncautoresetevent/) and Chris Gillum (https://stackoverflow.com/a/43012490)
|
|
public class AsyncAutoResetEvent
|
|
{
|
|
private readonly LinkedList<TaskCompletionSource<bool>> _waiters = new LinkedList<TaskCompletionSource<bool>>();
|
|
|
|
private bool _isSignaled;
|
|
|
|
public AsyncAutoResetEvent()
|
|
: this(false)
|
|
{
|
|
}
|
|
|
|
public AsyncAutoResetEvent(bool signaled)
|
|
{
|
|
_isSignaled = signaled;
|
|
}
|
|
|
|
public int WaitersCount
|
|
{
|
|
get
|
|
{
|
|
lock (_waiters)
|
|
{
|
|
return _waiters.Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Task<bool> WaitOneAsync()
|
|
{
|
|
return WaitOneAsync(CancellationToken.None);
|
|
}
|
|
|
|
public Task<bool> WaitOneAsync(TimeSpan timeout)
|
|
{
|
|
return WaitOneAsync(timeout, CancellationToken.None);
|
|
}
|
|
|
|
public Task<bool> WaitOneAsync(CancellationToken cancellationToken)
|
|
{
|
|
return WaitOneAsync(Timeout.InfiniteTimeSpan, cancellationToken);
|
|
}
|
|
|
|
public async Task<bool> WaitOneAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
TaskCompletionSource<bool> tcs;
|
|
|
|
lock (_waiters)
|
|
{
|
|
if (_isSignaled)
|
|
{
|
|
_isSignaled = false;
|
|
return true;
|
|
}
|
|
|
|
if (timeout == TimeSpan.Zero)
|
|
{
|
|
return _isSignaled;
|
|
}
|
|
|
|
tcs = new TaskCompletionSource<bool>();
|
|
_waiters.AddLast(tcs);
|
|
}
|
|
|
|
Task winner;
|
|
if (timeout == Timeout.InfiniteTimeSpan)
|
|
{
|
|
await tcs.Task.ConfigureAwait(false);
|
|
winner = tcs.Task;
|
|
}
|
|
else
|
|
{
|
|
winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken)).ConfigureAwait(false);
|
|
}
|
|
|
|
var taskWasSignaled = winner == tcs.Task;
|
|
if (taskWasSignaled)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// We timed-out; remove our reference to the task.
|
|
// This is an O(n) operation since waiters is a LinkedList<T>.
|
|
lock (_waiters)
|
|
{
|
|
_waiters.Remove(tcs);
|
|
|
|
if (winner.Status == TaskStatus.Canceled)
|
|
{
|
|
throw new OperationCanceledException(cancellationToken);
|
|
}
|
|
|
|
throw new TimeoutException();
|
|
}
|
|
}
|
|
|
|
public void Set()
|
|
{
|
|
TaskCompletionSource<bool> toRelease = null;
|
|
|
|
lock (_waiters)
|
|
{
|
|
if (_waiters.Count > 0)
|
|
{
|
|
// Signal the first task in the waiters list.
|
|
toRelease = _waiters.First.Value;
|
|
_waiters.RemoveFirst();
|
|
}
|
|
else if (!_isSignaled)
|
|
{
|
|
// No tasks are pending
|
|
_isSignaled = true;
|
|
}
|
|
}
|
|
|
|
toRelease?.SetResult(true);
|
|
}
|
|
}
|
|
}
|