mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-21 04:03:05 +08:00
1565 high cpu load from asyncsignal (#1566)
This commit is contained in:
@@ -52,12 +52,26 @@ namespace MQTTnet.Tests.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Loop_Signal()
|
public async Task Reset_Signal()
|
||||||
{
|
{
|
||||||
var asyncSignal = new AsyncSignal();
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
// WaitAsync should fail because no signal is available.
|
||||||
for (var i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)))
|
||||||
|
{
|
||||||
|
await asyncSignal.WaitAsync(timeout.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("This must fail because the signal is not yet set.");
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
asyncSignal.Set();
|
asyncSignal.Set();
|
||||||
|
|
||||||
// WaitAsync should return directly because the signal is available.
|
// WaitAsync should return directly because the signal is available.
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(90));
|
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(120));
|
||||||
|
|
||||||
Assert.AreEqual(100000, receivedMessages);
|
Assert.AreEqual(100000, receivedMessages);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
// Try to connect 50 clients at the same time.
|
// Try to connect 50 clients at the same time.
|
||||||
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||||
|
|
||||||
await Task.Delay(5000);
|
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||||
await sendClient.PublishStringAsync("aaa", "1");
|
await sendClient.PublishStringAsync("aaa", "1");
|
||||||
|
|
||||||
await Task.Delay(3000);
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
Assert.AreEqual(true, hasReceive);
|
Assert.AreEqual(true, hasReceive);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
lock (_syncRoot)
|
lock (_syncRoot)
|
||||||
{
|
{
|
||||||
_signal?.Dispose();
|
_signal.Dispose();
|
||||||
|
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
}
|
}
|
||||||
@@ -48,10 +48,10 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
if (_queue.TryDequeue(out var item))
|
if (_queue.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(true, item);
|
return AsyncQueueDequeueResult<TItem>.Success(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default);
|
return AsyncQueueDequeueResult<TItem>.NonSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken)
|
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken)
|
||||||
@@ -60,36 +60,42 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Task task;
|
Task task = null;
|
||||||
lock (_syncRoot)
|
lock (_syncRoot)
|
||||||
{
|
{
|
||||||
if (_isDisposed)
|
if (_isDisposed)
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default);
|
return AsyncQueueDequeueResult<TItem>.NonSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
task = _signal.WaitAsync(cancellationToken);
|
if (_queue.IsEmpty)
|
||||||
|
{
|
||||||
|
task = _signal.WaitAsync(cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await task.ConfigureAwait(false);
|
if (task != null)
|
||||||
|
{
|
||||||
|
await task.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default);
|
return AsyncQueueDequeueResult<TItem>.NonSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queue.TryDequeue(out var item))
|
if (_queue.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(true, item);
|
return AsyncQueueDequeueResult<TItem>.Success(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default);
|
return AsyncQueueDequeueResult<TItem>.NonSuccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default);
|
return AsyncQueueDequeueResult<TItem>.NonSuccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
namespace MQTTnet.Internal
|
namespace MQTTnet.Internal
|
||||||
{
|
{
|
||||||
public class AsyncQueueDequeueResult<TItem>
|
public sealed class AsyncQueueDequeueResult<TItem>
|
||||||
{
|
{
|
||||||
|
public static readonly AsyncQueueDequeueResult<TItem> NonSuccess = new AsyncQueueDequeueResult<TItem>(false, default);
|
||||||
|
|
||||||
public AsyncQueueDequeueResult(bool isSuccess, TItem item)
|
public AsyncQueueDequeueResult(bool isSuccess, TItem item)
|
||||||
{
|
{
|
||||||
IsSuccess = isSuccess;
|
IsSuccess = isSuccess;
|
||||||
@@ -15,5 +17,10 @@ namespace MQTTnet.Internal
|
|||||||
public bool IsSuccess { get; }
|
public bool IsSuccess { get; }
|
||||||
|
|
||||||
public TItem Item { get; }
|
public TItem Item { get; }
|
||||||
|
|
||||||
|
public static AsyncQueueDequeueResult<TItem> Success(TItem item)
|
||||||
|
{
|
||||||
|
return new AsyncQueueDequeueResult<TItem>(true, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,9 +33,17 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
_isSignaled = true;
|
_isSignaled = true;
|
||||||
|
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
// If there is already a waiting task let it run.
|
// If there is already a waiting task let it run.
|
||||||
_waiter?.Approve();
|
if (_waiter != null)
|
||||||
_waiter = null;
|
{
|
||||||
|
_waiter.Approve();
|
||||||
|
_waiter = null;
|
||||||
|
|
||||||
|
// Since we already got a waiter the signal must be reset right now!
|
||||||
|
_isSignaled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +55,11 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
if (_isSignaled)
|
if (_isSignaled)
|
||||||
{
|
{
|
||||||
|
_isSignaled = false;
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +69,7 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("Only one waiting task is permitted per async signal.");
|
throw new InvalidOperationException("Only one waiting task is permitted per async signal.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_waiter.Dispose();
|
_waiter.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +78,15 @@ namespace MQTTnet.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cleanup()
|
||||||
|
{
|
||||||
|
// Cleanup if the previous waiter was cancelled.
|
||||||
|
if (_waiter != null && _waiter.Task.IsCanceled)
|
||||||
|
{
|
||||||
|
_waiter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ThrowIfDisposed()
|
void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
if (_isDisposed)
|
if (_isDisposed)
|
||||||
|
|||||||
Reference in New Issue
Block a user