From 5090cfb411e063f12b92cb10b0738429ed2811af Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 5 Mar 2019 07:47:31 +0100 Subject: [PATCH] Fix memory leak in AsyncAutoResetEvent. --- .../MQTTnet/Internal/AsyncAutoResetEvent.cs | 24 +++++++++++++++++- .../AsyncAutoResentEventTests.cs | 25 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Source/MQTTnet/Internal/AsyncAutoResetEvent.cs b/Source/MQTTnet/Internal/AsyncAutoResetEvent.cs index a90b98e4..a6e96211 100644 --- a/Source/MQTTnet/Internal/AsyncAutoResetEvent.cs +++ b/Source/MQTTnet/Internal/AsyncAutoResetEvent.cs @@ -9,6 +9,7 @@ namespace MQTTnet.Internal public class AsyncAutoResetEvent { private readonly LinkedList> _waiters = new LinkedList>(); + private bool _isSignaled; public AsyncAutoResetEvent() @@ -21,6 +22,17 @@ namespace MQTTnet.Internal _isSignaled = signaled; } + public int WaitersCount + { + get + { + lock (_waiters) + { + return _waiters.Count; + } + } + } + public Task WaitOneAsync() { return WaitOneAsync(CancellationToken.None); @@ -59,7 +71,17 @@ namespace MQTTnet.Internal _waiters.AddLast(tcs); } - var winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken)).ConfigureAwait(false); + Task winner; + if (timeout == Timeout.InfiniteTimeSpan) + { + 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) { diff --git a/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs b/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs index 188f54fb..074b0099 100644 --- a/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs +++ b/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs @@ -17,6 +17,31 @@ namespace MQTTnet.Core.Tests _aare = new AsyncAutoResetEvent(); } + [TestMethod] + public async Task Cleanup_Waiters() + { + var @lock = new AsyncAutoResetEvent(); + + var waitOnePassed = false; + + Task.Run(async () => + { + await @lock.WaitOneAsync(TimeSpan.FromSeconds(2)); + waitOnePassed = true; + }); + + await Task.Delay(500); + + Assert.AreEqual(1, @lock.WaitersCount); + + @lock.Set(); + + await Task.Delay(1000); + + Assert.IsTrue(waitOnePassed); + Assert.AreEqual(0, @lock.WaitersCount); + } + [TestMethod] public async Task SingleThreadedPulse() {