mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-21 14:56:13 +00:00
Fix memory leak in AsyncAutoResetEvent (#229)
* Fix memory leak in AsyncAutoResetEvent CancellationTokenRegistration MUST be disposed, as the CancellationToken passed is saved for the lifetime of the Client, and registrations build up forever.
This commit is contained in:
parent
4c050744ad
commit
b13cff5a95
@ -106,6 +106,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
for(var i = 1; i <= 10; i++)
|
for(var i = 1; i <= 10; i++)
|
||||||
{
|
{
|
||||||
evnt.Set();
|
evnt.Set();
|
||||||
|
await Task.Delay(1); // Wait for the continuation.
|
||||||
Assert.That(10 - i == waiters.Count(w => w.Status != TaskStatus.RanToCompletion));
|
Assert.That(10 - i == waiters.Count(w => w.Status != TaskStatus.RanToCompletion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,44 +32,51 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// Wait for the AutoResetEvent to be set
|
/// Wait for the AutoResetEvent to be set
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<bool> WaitAsync(TimeSpan? timeout = null, CancellationToken ct = default)
|
public async Task<bool> WaitAsync(TimeSpan? timeout = null, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
lock (_waits)
|
CancellationTokenRegistration registration = default;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_signaled)
|
Task<bool> waiter = _completed;
|
||||||
|
lock (_waits)
|
||||||
{
|
{
|
||||||
if(_reset)
|
if (_signaled)
|
||||||
_signaled = false;
|
|
||||||
return _completed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ct.IsCancellationRequested)
|
|
||||||
return _completed;
|
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
if (timeout.HasValue)
|
|
||||||
{
|
{
|
||||||
var timeoutSource = new CancellationTokenSource(timeout.Value);
|
if (_reset)
|
||||||
var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, ct);
|
_signaled = false;
|
||||||
ct = cancellationSource.Token;
|
|
||||||
}
|
}
|
||||||
|
else if (!ct.IsCancellationRequested)
|
||||||
var registration = ct.Register(() =>
|
|
||||||
{
|
{
|
||||||
lock (_waits)
|
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
if (timeout.HasValue)
|
||||||
{
|
{
|
||||||
tcs.TrySetResult(false);
|
var timeoutSource = new CancellationTokenSource(timeout.Value);
|
||||||
|
var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, ct);
|
||||||
// Not the cleanest but it works
|
ct = cancellationSource.Token;
|
||||||
_waits = new Queue<TaskCompletionSource<bool>>(_waits.Where(i => i != tcs));
|
|
||||||
}
|
}
|
||||||
}, useSynchronizationContext: false);
|
|
||||||
|
registration = ct.Register(() =>
|
||||||
|
{
|
||||||
|
lock (_waits)
|
||||||
|
{
|
||||||
|
tcs.TrySetResult(false);
|
||||||
|
|
||||||
|
// Not the cleanest but it works
|
||||||
|
_waits = new Queue<TaskCompletionSource<bool>>(_waits.Where(i => i != tcs));
|
||||||
|
}
|
||||||
|
}, useSynchronizationContext: false);
|
||||||
|
|
||||||
|
|
||||||
_waits.Enqueue(tcs);
|
_waits.Enqueue(tcs);
|
||||||
return tcs.Task;
|
waiter = tcs.Task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await waiter.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
registration.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user