Skip to content

Commit

Permalink
Protocol: add MatchActionException helper class. (#223)
Browse files Browse the repository at this point in the history
This helps users identify the Observer/Connection disposed cases.
  • Loading branch information
tmds committed Jan 19, 2024
1 parent c7eb055 commit 217a214
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/Tmds.DBus.Protocol/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Tmds.DBus.Protocol;

public partial class Connection : IDisposable
{
private static readonly Exception s_disposedSentinel = new ObjectDisposedException(typeof(Connection).FullName);
internal static readonly Exception DisposedException = new ObjectDisposedException(typeof(Connection).FullName);
private static Connection? s_systemConnection;
private static Connection? s_sessionConnection;

Expand Down Expand Up @@ -150,7 +150,7 @@ public void Dispose()
_disposed = true;
}

Disconnect(s_disposedSentinel);
Disconnect(DisposedException);
}

internal void Disconnect(Exception disconnectReason, DBusConnection? trigger = null)
Expand Down
7 changes: 4 additions & 3 deletions src/Tmds.DBus.Protocol/DBusConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public void Invoke(Exception? exception, Message message)
private readonly Dictionary<string, MatchMaker> _matchMakers;
private readonly List<Observer> _matchedObservers;
private readonly Dictionary<string, IMethodHandler> _pathHandlers;
private readonly string? _machineId;
private readonly string _machineId;

private IMessageStream? _messageStream;
private ConnectionState _state;
Expand Down Expand Up @@ -845,9 +845,10 @@ MessageBuffer CreateAddMatchMessage(string ruleString)
}
}

internal static readonly ObjectDisposedException ObserverDisposedException = new ObjectDisposedException(typeof(Observer).FullName);

sealed class Observer : IDisposable
{
private static readonly ObjectDisposedException s_objectDisposedException = new ObjectDisposedException(typeof(Observer).FullName);
private readonly object _gate = new object();
private readonly SynchronizationContext? _synchronizationContext;
private readonly MatchMaker _matchMaker;
Expand All @@ -864,7 +865,7 @@ public Observer(SynchronizationContext? synchronizationContext, MatchMaker match
Subscribes = subscribes;
}

public void Dispose() => Dispose(s_objectDisposedException);
public void Dispose() => Dispose(ObserverDisposedException);

public void Dispose(Exception? exception, bool removeObserver = true)
{
Expand Down
17 changes: 17 additions & 0 deletions src/Tmds.DBus.Protocol/MatchActionException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Tmds.DBus.Protocol;

public static class MatchActionException
{
// Exception used when the IDisposable returned by AddMatchAsync gets disposed.
public static bool IsObserverDisposed(Exception exception)
=> object.ReferenceEquals(exception, DBusConnection.ObserverDisposedException);

// Exception used when the Connection gets disposed.
public static bool IsConnectionDisposed(Exception exception)
// note: Connection.DisposedException is only ever used as an InnerException of DisconnectedException,
// so we directly check for that.
=> object.ReferenceEquals(exception?.InnerException, Connection.DisposedException);

public static bool IsDisposed(Exception exception)
=> IsObserverDisposed(exception) || IsConnectionDisposed(exception);
}
53 changes: 53 additions & 0 deletions test/Tmds.DBus.Protocol.Tests/ExceptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace Tmds.DBus.Protocol.Tests
{
public class ExceptionTests
{
[Fact]
public async Task ObserverDisposed()
{
var connections = PairedConnection.CreatePair();
using var conn1 = connections.Item1;
using var conn2 = connections.Item2;

TaskCompletionSource<Exception> tcs = new();

var disposable = await conn1.AddMatchAsync(
new MatchRule(), (Message message, object? state) => "", (Exception? ex, string s, object? s1, object? s2) =>
{
tcs.SetResult(ex!);
});

disposable.Dispose();

Exception ex = await tcs.Task;
Assert.True(MatchActionException.IsObserverDisposed(ex));
Assert.True(MatchActionException.IsDisposed(ex));
}

[Fact]
public async Task ConnectionDisposed()
{
var connections = PairedConnection.CreatePair();
using var conn1 = connections.Item1;
using var conn2 = connections.Item2;

TaskCompletionSource<Exception> tcs = new();

var disposable = await conn1.AddMatchAsync(
new MatchRule(), (Message message, object? state) => "", (Exception? ex, string s, object? s1, object? s2) =>
{
tcs.SetResult(ex!);
});

conn1.Dispose();

Exception ex = await tcs.Task;
Assert.True(MatchActionException.IsConnectionDisposed(ex));
Assert.True(MatchActionException.IsDisposed(ex));
}
}
}

0 comments on commit 217a214

Please sign in to comment.