feat(app): observe canonical placement receipts
This commit is contained in:
parent
378ca95a67
commit
f05ed5c3cd
14 changed files with 545 additions and 31 deletions
102
src/AcDream.App/Net/GraphicalSessionEventRoute.cs
Normal file
102
src/AcDream.App/Net/GraphicalSessionEventRoute.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Physics;
|
||||
using AcDream.Runtime.Session;
|
||||
|
||||
namespace AcDream.App.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Owns the inbound route, canonical placement observer, and update-thread
|
||||
/// retry lease for one exact graphical session generation.
|
||||
/// </summary>
|
||||
internal sealed class GraphicalSessionEventRoute : ILiveSessionEventRouting
|
||||
{
|
||||
private readonly ILiveSessionEventRouting _events;
|
||||
private readonly Func<RuntimePlacementProjectionSubscription>
|
||||
_createSubscription;
|
||||
private readonly Func<RuntimeGenerationToken> _generation;
|
||||
private readonly RuntimePlacementProjectionRetrySlot _retries;
|
||||
private RuntimePlacementProjectionSubscription? _subscription;
|
||||
private IDisposable? _retryLease;
|
||||
private bool _attachStarted;
|
||||
private bool _eventsDisposed;
|
||||
private bool _disposed;
|
||||
|
||||
internal GraphicalSessionEventRoute(
|
||||
ILiveSessionEventRouting events,
|
||||
GameRuntime runtime,
|
||||
IRuntimePlacementProjectionSink placements,
|
||||
RuntimePlacementProjectionRetrySlot retries)
|
||||
: this(
|
||||
events,
|
||||
() => new RuntimePlacementProjectionSubscription(
|
||||
runtime,
|
||||
placements,
|
||||
retryPendingOnSubscribe: false),
|
||||
() => runtime.Generation,
|
||||
retries)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
ArgumentNullException.ThrowIfNull(placements);
|
||||
}
|
||||
|
||||
internal GraphicalSessionEventRoute(
|
||||
ILiveSessionEventRouting events,
|
||||
Func<RuntimePlacementProjectionSubscription> createSubscription,
|
||||
Func<RuntimeGenerationToken> generation,
|
||||
RuntimePlacementProjectionRetrySlot retries)
|
||||
{
|
||||
_events = events ?? throw new ArgumentNullException(nameof(events));
|
||||
_createSubscription = createSubscription
|
||||
?? throw new ArgumentNullException(nameof(createSubscription));
|
||||
_generation = generation
|
||||
?? throw new ArgumentNullException(nameof(generation));
|
||||
_retries = retries ?? throw new ArgumentNullException(nameof(retries));
|
||||
}
|
||||
|
||||
public void Attach()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
if (_attachStarted)
|
||||
return;
|
||||
|
||||
_attachStarted = true;
|
||||
_events.Attach();
|
||||
|
||||
RuntimePlacementProjectionSubscription? subscription = null;
|
||||
IDisposable? retryLease = null;
|
||||
try
|
||||
{
|
||||
subscription = _createSubscription();
|
||||
retryLease = _retries.BindOwned(
|
||||
_generation(),
|
||||
subscription.RetryPending);
|
||||
_subscription = subscription;
|
||||
_retryLease = retryLease;
|
||||
_ = subscription.RetryPending();
|
||||
}
|
||||
catch
|
||||
{
|
||||
retryLease?.Dispose();
|
||||
subscription?.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
// Unpublish the frame callback before detaching the observer. A frame
|
||||
// can therefore never retry a retired generation or disposed route.
|
||||
Interlocked.Exchange(ref _retryLease, null)?.Dispose();
|
||||
Interlocked.Exchange(ref _subscription, null)?.Dispose();
|
||||
if (!_eventsDisposed)
|
||||
{
|
||||
_events.Dispose();
|
||||
_eventsDisposed = true;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue