Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak inside AsyncSignal / AsyncSignalWaiter #1584

Closed
mario-zelger opened this issue Nov 24, 2022 · 0 comments
Closed

Memory leak inside AsyncSignal / AsyncSignalWaiter #1584

mario-zelger opened this issue Nov 24, 2022 · 0 comments
Labels
bug Something isn't working

Comments

@mario-zelger
Copy link
Contributor

mario-zelger commented Nov 24, 2022

Describe the bug

When calling the Set() Method of an AsyncSignal Object the AsyncSignalWaiter is not correctly disposed:

public void Set()
{
    lock (_syncRoot)
    {
        _isSignaled = true;

        Cleanup();
        
        // If there is already a waiting task let it run.
        if (_waiter != null)
        {
            _waiter.Approve();
            _waiter = null; // Waiter is never disposed

            // Since we already got a waiter the signal must be reset right now!
            _isSignaled = false;
        }
    }
}

When I dispose the AsyncSignalWaiter after the approval inside Set() or inside the Approve() or Cancel() method of the AsyncSignalWaiter no memory leaks can be found (see screenshots).

public void Set()
{
    lock (_syncRoot)
    {
        _isSignaled = true;

        Cleanup();
        
        // If there is already a waiting task let it run.
        if (_waiter != null)
        {
            _waiter.Approve();
            _waiter.Dispose(); // Dispose waiter
            _waiter = null;

            // Since we already got a waiter the signal must be reset right now!
            _isSignaled = false;
        }
    }
}
public void Approve()
{
    _promise.TrySetResult(true);
    Dispose(); // Dispose itself
}

public void Dispose()
{
    _cancellationTokenRegistration.Dispose();

    _promise.TrySetException(new ObjectDisposedException(nameof(AsyncSignalWaiter)));
}

void Cancel()
{
    _promise.TrySetCanceled();
    Dispose();  // Dispose itself
}

Which component is your bug related to?

  • Client
  • ManagedClient
  • RpcClient (unsure)

To Reproduce

Steps to reproduce the behavior:

  1. Using version 4.1.3.436 of MQTTnet or the master branch.
  2. Run a simple console app with either a Client or ManagedClient (see code below).
  3. Run a memory profiler for some time (JetBrains dotMemory in my case)
  4. Watch Gen 2 Heap growing and growing.

Expected behavior

The CancellationTokenRegistration inside the AsyncSignalWaiter should always be disposed.

Screenshots

Without disposing the AsyncSignalWaiter
image

With disposing of the AsyncSignalWaiter
image

Code example

Simple console app used for memory profiling:

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Packets;

var clientOptions = new ManagedMqttClientOptionsBuilder()
    .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
    .WithClientOptions(new MqttClientOptionsBuilder()
        .WithClientId(Guid.NewGuid().ToString())
        .WithTcpServer("localhost")
        .Build())
    .Build();

using var mqttClient = new MqttFactory().CreateManagedMqttClient();
await mqttClient.StartAsync(clientOptions);

var topic = new MqttTopicFilterBuilder()
    .WithTopic("#")
    .Build();
await mqttClient.SubscribeAsync(new List<MqttTopicFilter> { topic });

Console.ReadLine();

await mqttClient.StopAsync();
@mario-zelger mario-zelger added the bug Something isn't working label Nov 24, 2022
AndreasFoerster added a commit to siemens/opc-ua-pubsub-dotnet that referenced this issue Nov 29, 2022
AndreasFoerster added a commit to siemens/opc-ua-pubsub-dotnet that referenced this issue Nov 29, 2022
* Publish(): no endless wait for PublishAsync()

* MQTTnet update V4.1.1 -> V4.1.3 (but please not dotnet/MQTTnet#1584)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants