Merge branch 'develop' into connectErrors

This commit is contained in:
Jim Schaad
2020-01-08 11:10:58 -08:00
6 changed files with 49 additions and 375 deletions
@@ -88,5 +88,17 @@ namespace MQTTnet.Implementations
#endif
}
public static Task CompletedTask
{
get
{
#if NET452
return Task.FromResult(0);
#else
return Task.CompletedTask;
#endif
}
}
}
}
@@ -1,131 +0,0 @@
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)
{
using (cancellationToken.Register(() => { tcs.TrySetCanceled(); }))
{
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?.TrySetResult(true);
}
}
}
+3 -2
View File
@@ -44,12 +44,13 @@ namespace MQTTnet.Internal
{
return;
}
_isDisposed = true;
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
_isDisposed = true;
}
#endregion
}
@@ -9,7 +9,7 @@ namespace MQTTnet.PacketDispatcher
{
public sealed class MqttPacketAwaiter<TPacket> : Disposable, IMqttPacketAwaiter where TPacket : MqttBasePacket
{
private readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource = new TaskCompletionSource<MqttBasePacket>();
private readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource;
private readonly ushort? _packetIdentifier;
private readonly MqttPacketDispatcher _owningPacketDispatcher;
@@ -17,13 +17,18 @@ namespace MQTTnet.PacketDispatcher
{
_packetIdentifier = packetIdentifier;
_owningPacketDispatcher = owningPacketDispatcher ?? throw new ArgumentNullException(nameof(owningPacketDispatcher));
#if NET452
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>();
#else
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(TaskCreationOptions.RunContinuationsAsynchronously);
#endif
}
public async Task<TPacket> WaitOneAsync(TimeSpan timeout)
{
using (var timeoutToken = new CancellationTokenSource(timeout))
{
timeoutToken.Token.Register(() => _taskCompletionSource.TrySetException(new MqttCommunicationTimedOutException()));
timeoutToken.Token.Register(() => Fail(new MqttCommunicationTimedOutException()));
var packet = await _taskCompletionSource.Task.ConfigureAwait(false);
return (TPacket)packet;
@@ -33,24 +38,47 @@ namespace MQTTnet.PacketDispatcher
public void Complete(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetResult(packet));
#else
_taskCompletionSource.TrySetResult(packet);
#endif
}
public void Fail(Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetException(exception));
#else
_taskCompletionSource.TrySetException(exception);
#endif
}
public void Cancel()
{
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetCanceled());
#else
_taskCompletionSource.TrySetCanceled();
#endif
}
protected override void Dispose(bool disposing)
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.Internal;
namespace MQTTnet.Server
@@ -21,7 +22,7 @@ namespace MQTTnet.Server
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttRetainedMessagesManager));
_options = options ?? throw new ArgumentNullException(nameof(options));
return Task.CompletedTask;
return PlatformAbstractionLayer.CompletedTask;
}
public async Task LoadMessagesAsync()