88 lines
3.1 KiB
C#
88 lines
3.1 KiB
C#
using AcDream.Runtime.Entities;
|
|
|
|
namespace AcDream.Runtime.Physics;
|
|
|
|
/// <summary>
|
|
/// Public host boundary for Runtime-owned SetPosition projection receipts.
|
|
/// The channel owns no placement state: observation, retry, and exact-token
|
|
/// acknowledgement delegate to the canonical entity lifetime's event stream
|
|
/// and SetPosition owner.
|
|
/// </summary>
|
|
public sealed class RuntimePlacementProjectionChannel
|
|
{
|
|
private readonly RuntimeEntityObjectEventStream _events;
|
|
private readonly RuntimeSetPositionState _setPosition;
|
|
private Func<RuntimeGenerationToken> _generation = static () => default;
|
|
private bool _generationBound;
|
|
|
|
internal RuntimePlacementProjectionChannel(
|
|
RuntimeEntityObjectEventStream events,
|
|
RuntimeSetPositionState setPosition)
|
|
{
|
|
_events = events ?? throw new ArgumentNullException(nameof(events));
|
|
_setPosition = setPosition
|
|
?? throw new ArgumentNullException(nameof(setPosition));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Observes ordered immutable projection receipts on the Runtime commit
|
|
/// thread. A host must acknowledge only after its projection succeeds.
|
|
/// </summary>
|
|
public IDisposable Subscribe(IRuntimePlacementObserver observer) =>
|
|
_events.SubscribePlacement(observer);
|
|
|
|
internal void BindGeneration(Func<RuntimeGenerationToken> generation)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(generation);
|
|
if (_generationBound)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"The Runtime placement generation source is already bound.");
|
|
}
|
|
|
|
_generation = generation;
|
|
_generationBound = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acknowledges only the exact oldest outstanding receipt. Stale,
|
|
/// reordered, superseded, or already acknowledged tokens are rejected.
|
|
/// </summary>
|
|
public bool Acknowledge(
|
|
RuntimeGenerationToken expectedGeneration,
|
|
in RuntimePlacementProjectionToken token) =>
|
|
IsCurrent(expectedGeneration)
|
|
&& _setPosition.AcknowledgeProjection(token);
|
|
|
|
/// <summary>
|
|
/// Republishes every still-pending immutable receipt in canonical order.
|
|
/// Runtime authority is never replayed or recommitted by a retry.
|
|
/// </summary>
|
|
public bool RetryPending(RuntimeGenerationToken expectedGeneration)
|
|
{
|
|
if (!IsCurrent(expectedGeneration))
|
|
return false;
|
|
_setPosition.RetryPendingProjections();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the exact oldest outstanding receipt without consuming it.
|
|
/// </summary>
|
|
public bool TryPeek(
|
|
RuntimeGenerationToken expectedGeneration,
|
|
out RuntimePlacementProjectionSnapshot projection)
|
|
{
|
|
if (IsCurrent(expectedGeneration))
|
|
return _setPosition.TryPeekProjection(out projection);
|
|
projection = default;
|
|
return false;
|
|
}
|
|
|
|
public int PendingCount => _setPosition.PendingProjectionCount;
|
|
|
|
private bool IsCurrent(RuntimeGenerationToken expectedGeneration) =>
|
|
_generationBound
|
|
&& expectedGeneration.Value != 0UL
|
|
&& expectedGeneration == _generation();
|
|
}
|