mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 17:23:04 +08:00
There isn't any code in MQTTnet that actually uses a cancellation token in WaitAsync, so this is more of a preventative thing than a bug fix. The original code just checks if the task was completed, not whether it was cancelled. If the process is cancelled immediately before the call to WaitAsync, it'll return as normal (https://referencesource.microsoft.com/#mscorlib/system/threading/SemaphoreSlim.cs,612) rather than throw a cancellation exception. This change will ensure we only return the releaser if the wait actually ran to completion rather than exited early due to cancellation. I've tested this, and it properly throws a cancellation exception later on.
58 lines
1.6 KiB
C#
58 lines
1.6 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MQTTnet.Internal
|
|
{
|
|
// From Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6-asynclock/)
|
|
public class AsyncLock : IDisposable
|
|
{
|
|
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
|
private readonly Task<IDisposable> _releaser;
|
|
|
|
public AsyncLock()
|
|
{
|
|
_releaser = Task.FromResult((IDisposable)new Releaser(this));
|
|
}
|
|
|
|
public Task<IDisposable> WaitAsync()
|
|
{
|
|
return WaitAsync(CancellationToken.None);
|
|
}
|
|
|
|
public Task<IDisposable> WaitAsync(CancellationToken cancellationToken)
|
|
{
|
|
var task = _semaphore.WaitAsync(cancellationToken);
|
|
if (task.Status == TaskStatus.RanToCompletion)
|
|
{
|
|
return _releaser;
|
|
}
|
|
|
|
return task.ContinueWith(
|
|
(_, state) => (IDisposable)state,
|
|
_releaser.Result,
|
|
cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_semaphore?.Dispose();
|
|
}
|
|
|
|
private class Releaser : IDisposable
|
|
{
|
|
private readonly AsyncLock _toRelease;
|
|
|
|
internal Releaser(AsyncLock toRelease)
|
|
{
|
|
_toRelease = toRelease;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_toRelease._semaphore.Release();
|
|
}
|
|
}
|
|
}
|
|
}
|