HavenDV / H.Pipes

A simple, easy to use, strongly-typed, async wrapper around .NET named pipes.
MIT License
242 stars 26 forks source link

Cannot access a disposed object. #45

Open michal-paukert-tfs opened 9 months ago

michal-paukert-tfs commented 9 months ago

Describe the bug

When my client was disconnected it throws exception My usage:

    private readonly IPipeClient<PipeMessage> _client;

    private async void ClientOnDisconnected(object? _, ConnectionEventArgs<PipeMessage> e)
    {
        _logger.LogInformation("IPC client name {name} was disconnected", e.Connection.PipeName);
        while (!_client.IsConnected)
        {
            using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
            try
            {
                await Start(cancellationTokenSource.Token).ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                _logger.LogWarning(exception,"Failed to reconnect");
            }
        }
    }

    public async Task Start(CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Connecting to IPC {pipeName}", _client.PipeName);
        await _client.ConnectAsync(cancellationToken);
    }

Steps to reproduce the bug

Just stop IPC server

System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Timers.Timer'. at void System.Timers.Timer.setEnabled(bool value) at async Task H.Pipes.PipeClient.ConnectAsync(CancellationToken cancellationToken) in //src/libs/H.Pipes/PipeClient.cs:line 173 at async Task Ipc.Client.BaseIpcClient.Start(CancellationToken cancellationToken) in //src/Ipc/Client/BaseIpcClient.cs:line 141 at async void Ipc.Client.BaseIpcClient.ClientOnDisconnected(object , ConnectionEventArgs e) in /_/src/Ipc/Client/BaseIpcClient.cs:line 26

Expected behavior

No response

Screenshots

No response

NuGet package version

2.0.59

Platform

Console

IDE

Visual Studio 2022

Additional context

No response

HavenDV commented 9 months ago

I couldn't understand your use case. Now any client will reconnect to the server by default. I created a separate test for this You can help if you correct this test for your case so that it fails

https://github.com/HavenDV/H.Pipes/commit/2a753544496fd4be9887ebed10f30c58ac37000c

PipeName: rcs
Server starting...
Server is started!
04:57:43.182: Waiting 1 second
04:57:43.191: Client rcs_8c762acf-9519-4139-9bdf-657dfd04be75 is now connected!
04:57:44.184: Stopping server
04:57:44.187: Client rcs_8c762acf-9519-4139-9bdf-657dfd04be75 disconnected
04:57:44.188: Starting server
04:57:44.189: Waiting 3 seconds
04:57:44.283: Client rcs_937a7e64-d42c-4513-9696-3406e3448705 is now connected!
04:57:47.189: Send data to clients
04:57:47.222: Client rcs_937a7e64-d42c-4513-9696-3406e3448705 disconnected
michal-paukert-tfs commented 9 months ago

It is weird I am not able to reproduce again I have found this in my logs so it looks like it works.

2024-01-08 10:08:36.944 +01:00 [Information] [NotificationAgent.IPC.NotificationAgentIpcClient] IPC client name "NotificationAgentClientIPC_0e54ee7e-212a-4f8b-aef8-f95dc4349ca5" was disconnected
2024-01-08 10:08:36.944 +01:00 [Information] [NotificationAgent.IPC.NotificationAgentIpcClient] Connecting to IPC "NotificationAgentClientIPC"
2024-01-08 10:08:38.652 +01:00 [Information] [NotificationAgent.IPC.NotificationClientApiIpcClient] Connecting to IPC "NotificationApiClientIPC"
2024-01-08 10:08:39.960 +01:00 [Information] [NotificationAgent.IPC.NotificationAgentIpcClient] Connecting to IPC "NotificationAgentClientIPC"
2024-01-08 10:08:40.583 +01:00 [Information] [NotificationAgent.IPC.NotificationAgentIpcClient] IPC client name "NotificationAgentClientIPC_edad42cf-3601-435a-aa0b-d2831a336725" was connected

I will try to reproduce again otherwise I will close this issue.

michal-paukert-tfs commented 5 months ago

Reproduced again. There is a code which reproduced that.

        private async void ClientOnDisconnected(object? _, ConnectionEventArgs<PipeMessage> e)
        {
            Logger.LogInformation("IPC client name {name} was disconnected", e.Connection.PipeName);
            while (!_client.IsConnected)
            {
                using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
                try
                {
                    await Start(cancellationTokenSource.Token).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    //Do not log when trying to reconnect
                }
                catch (ObjectDisposedException)
                {
                    Logger.LogWarning("IPC client disposed");
                    DebugUtils.CreateFullMemoryDump();
                }
                catch (Exception exception)
                {
                    Logger.LogWarning(exception,"Failed to reconnect");
                }
            }
        }

I wonder why there it hit General Exception instead of ObjectDisposedException

        2024-04-11 16:35:30.413 +02:00 [Warning] [SoftwareAgent.API.IPC.SoftwareAgentIpcClient] Failed to reconnect
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Timers.Timer'.
   at void System.Timers.Timer.set_Enabled(bool value)
   at async Task H.Pipes.PipeClient<T>.ConnectAsync(CancellationToken cancellationToken) in /_/src/libs/H.Pipes/PipeClient.cs:line 173
   at async Task Ipc.Client.BaseIpcClient.Start(CancellationToken cancellationToken) in /_/src/Ipc/Client/BaseIpcClient.cs:line 141
   at async void Ipc.Client.BaseIpcClient.ClientOnDisconnected(object _, ConnectionEventArgs<PipeMessage> e) in /_/src/Ipc/Client/BaseIpcClient.cs:line 26

@HavenDV