refactor(net): own session wiring subscriptions
This commit is contained in:
parent
88fe1db37b
commit
7d452aa6a2
8 changed files with 577 additions and 61 deletions
61
src/AcDream.Core.Net/SubscriptionSet.cs
Normal file
61
src/AcDream.Core.Net/SubscriptionSet.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
namespace AcDream.Core.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Owns a transactionally-built set of subscriptions and releases them in
|
||||
/// reverse registration order. Disposal is idempotent and attempts every
|
||||
/// release even when one fails.
|
||||
/// </summary>
|
||||
internal sealed class SubscriptionSet : IDisposable
|
||||
{
|
||||
private List<IDisposable>? _subscriptions = [];
|
||||
|
||||
public void Add(IDisposable subscription)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(subscription);
|
||||
List<IDisposable>? subscriptions = _subscriptions;
|
||||
if (subscriptions is null)
|
||||
{
|
||||
subscription.Dispose();
|
||||
throw new ObjectDisposedException(nameof(SubscriptionSet));
|
||||
}
|
||||
|
||||
subscriptions.Add(subscription);
|
||||
}
|
||||
|
||||
public void Add(Action unsubscribe)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(unsubscribe);
|
||||
Add(new ActionSubscription(unsubscribe));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
List<IDisposable>? subscriptions =
|
||||
Interlocked.Exchange(ref _subscriptions, null);
|
||||
if (subscriptions is null)
|
||||
return;
|
||||
|
||||
List<Exception>? errors = null;
|
||||
for (int i = subscriptions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
try
|
||||
{
|
||||
subscriptions[i].Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(errors ??= []).Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors is not null)
|
||||
throw new AggregateException("one or more subscriptions failed to detach", errors);
|
||||
}
|
||||
|
||||
private sealed class ActionSubscription(Action unsubscribe) : IDisposable
|
||||
{
|
||||
private Action? _unsubscribe = unsubscribe;
|
||||
|
||||
public void Dispose() => Interlocked.Exchange(ref _unsubscribe, null)?.Invoke();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue