This commit is contained in:
Christian
2022-02-20 21:10:46 +01:00
committed by GitHub
parent 47e674e4d3
commit 2ce9b94ec9
608 changed files with 22364 additions and 19420 deletions
+88
View File
@@ -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();
}
}
}
+5 -1
View File
@@ -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;
+5 -1
View File
@@ -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
}
}
}
+5 -1
View File
@@ -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;
+5 -1
View File
@@ -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
{
+140
View File
@@ -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
}
}
+5 -25
View File
@@ -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;
}
}
}
}
}
+5 -2
View File
@@ -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
{
+5 -1
View File
@@ -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;