mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-30 10:42:48 +08:00
v4.0.0 (#1357)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class AsyncEvent<TEventArgs> where TEventArgs : EventArgs
|
||||
{
|
||||
readonly List<AsyncEventInvocator<TEventArgs>> _handlers = new List<AsyncEventInvocator<TEventArgs>>();
|
||||
|
||||
public bool HasHandlers => _handlers.Count > 0;
|
||||
|
||||
public void AddHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(null, handler));
|
||||
}
|
||||
|
||||
public void AddHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(handler, null));
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(TEventArgs eventArgs)
|
||||
{
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
await handler.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||
}
|
||||
|
||||
public void RemoveHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||
}
|
||||
|
||||
public async Task TryInvokeAsync(TEventArgs eventArgs, MqttNetSourceLogger logger)
|
||||
{
|
||||
if (eventArgs == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(eventArgs));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.Warning(exception, $"Error while invoking event ({typeof(TEventArgs)}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public readonly struct AsyncEventInvocator<TEventArgs>
|
||||
{
|
||||
readonly Action<TEventArgs> _handler;
|
||||
readonly Func<TEventArgs, Task> _asyncHandler;
|
||||
|
||||
public AsyncEventInvocator(Action<TEventArgs> handler, Func<TEventArgs, Task> asyncHandler)
|
||||
{
|
||||
_handler = handler;
|
||||
_asyncHandler = asyncHandler;
|
||||
}
|
||||
|
||||
public bool WrapsHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
return ReferenceEquals(_handler, handler);
|
||||
}
|
||||
|
||||
public bool WrapsHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
return ReferenceEquals(_asyncHandler, handler);
|
||||
}
|
||||
|
||||
public Task InvokeAsync(TEventArgs eventArgs)
|
||||
{
|
||||
if (_handler != null)
|
||||
{
|
||||
_handler.Invoke(eventArgs);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
if (_asyncHandler != null)
|
||||
{
|
||||
return _asyncHandler.Invoke(eventArgs);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Internal
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public class AsyncQueueDequeueResult<TItem>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class AsyncTaskCompletionSource<TResult>
|
||||
{
|
||||
readonly TaskCompletionSource<TResult> _taskCompletionSource;
|
||||
|
||||
public AsyncTaskCompletionSource()
|
||||
{
|
||||
#if NET452
|
||||
_taskCompletionSource = new TaskCompletionSource<TResult>();
|
||||
#else
|
||||
_taskCompletionSource = new TaskCompletionSource<TResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
#endif
|
||||
}
|
||||
|
||||
public Task<TResult> Task => _taskCompletionSource.Task;
|
||||
|
||||
public bool TrySetCanceled()
|
||||
{
|
||||
#if NET452
|
||||
// To prevent deadlocks it is required to call the _TrySetCanceled_ 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
|
||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetCanceled());
|
||||
return true;
|
||||
#else
|
||||
return _taskCompletionSource.TrySetCanceled();
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool TrySetException(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
#if NET452
|
||||
// To prevent deadlocks it is required to call the _TrySetException_ 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
|
||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetException(exception));
|
||||
return true;
|
||||
#else
|
||||
return _taskCompletionSource.TrySetException(exception);
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool TrySetResult(TResult result)
|
||||
{
|
||||
#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
|
||||
System.Threading.Tasks.Task.Run(() => _taskCompletionSource.TrySetResult(result));
|
||||
return true;
|
||||
#else
|
||||
return _taskCompletionSource.TrySetResult(result);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class MqttPacketBus : IDisposable
|
||||
{
|
||||
readonly LinkedList<MqttPacketBusItem>[] _partitions =
|
||||
{
|
||||
new LinkedList<MqttPacketBusItem>(),
|
||||
new LinkedList<MqttPacketBusItem>(),
|
||||
new LinkedList<MqttPacketBusItem>()
|
||||
};
|
||||
|
||||
readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
|
||||
readonly object _syncRoot = new object();
|
||||
|
||||
int _activePartition = (int)MqttPacketBusPartition.Health;
|
||||
|
||||
public int ItemsCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions.Sum(p => p.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
foreach (var partition in _partitions)
|
||||
{
|
||||
partition.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttPacketBusItem> DequeueItemAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
MoveActivePartition();
|
||||
|
||||
if (_partitions[_activePartition].Count > 0)
|
||||
{
|
||||
var item = _partitions[_activePartition].First;
|
||||
_partitions[_activePartition].RemoveFirst();
|
||||
return item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No partition contains data so that we have to wait and put
|
||||
// the worker back to the thread pool.
|
||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
throw new InvalidOperationException("MqttPacketBus is broken.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_semaphore?.Dispose();
|
||||
}
|
||||
|
||||
public void DropFirstItem(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
var partitionInstance = _partitions[(int)partition];
|
||||
|
||||
if (partitionInstance.Any())
|
||||
{
|
||||
partitionInstance.RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueueItem(MqttPacketBusItem item, MqttPacketBusPartition partition)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_partitions[(int)partition].AddLast(item);
|
||||
}
|
||||
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
public List<MqttPacket> ExportPackets(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions[(int)partition].Select(i => i.Packet).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public int PartitionItemsCount(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions[(int)partition].Count;
|
||||
}
|
||||
}
|
||||
|
||||
void MoveActivePartition()
|
||||
{
|
||||
if (_activePartition >= _partitions.Length - 1)
|
||||
{
|
||||
_activePartition = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_activePartition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class MqttPacketBusItem
|
||||
{
|
||||
readonly AsyncTaskCompletionSource<int> _promise = new AsyncTaskCompletionSource<int>();
|
||||
|
||||
public MqttPacketBusItem(MqttPacket packet)
|
||||
{
|
||||
Packet = packet ?? throw new ArgumentNullException(nameof(packet));
|
||||
}
|
||||
|
||||
public MqttPacket Packet { get; }
|
||||
|
||||
public event EventHandler Delivered;
|
||||
|
||||
public Task WaitForDeliveryAsync()
|
||||
{
|
||||
return _promise.Task;
|
||||
}
|
||||
|
||||
public void MarkAsDelivered()
|
||||
{
|
||||
if (_promise.TrySetResult(0))
|
||||
{
|
||||
Delivered?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkAsFailed(Exception exception)
|
||||
{
|
||||
if (exception == null) throw new ArgumentNullException(nameof(exception));
|
||||
|
||||
_promise.TrySetException(exception);
|
||||
}
|
||||
|
||||
public void MarkAsCancelled()
|
||||
{
|
||||
_promise.TrySetCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public enum MqttPacketBusPartition
|
||||
{
|
||||
Data,
|
||||
|
||||
Control,
|
||||
|
||||
Health
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Exceptions;
|
||||
@@ -30,29 +34,5 @@ namespace MQTTnet.Internal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<TResult> WaitAsync<TResult>(Func<CancellationToken, Task<TResult>> action, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
|
||||
using (var timeoutCts = new CancellationTokenSource(timeout))
|
||||
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action(linkedCts.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException exception)
|
||||
{
|
||||
var timeoutReached = timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
|
||||
if (timeoutReached)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException(exception);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using MQTTnet.Diagnostics;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System.IO;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
Reference in New Issue
Block a user