Class PacePlayer
Controls the pace execution of a task.
public class PacePlayer : IDisposable
- Inheritance
-
PacePlayer
- Implements
- Inherited Members
- Extension Methods
Constructors
PacePlayer(ILogger)
Initializes a new instance with the specified logger for exception reporting.
public PacePlayer(ILogger logger)
Parameters
loggerILoggerLogger used by the player to report exceptions raised during pace execution.
Properties
CancellationToken
Gets the cancellation token for the running task; None while no run is live.
public CancellationToken CancellationToken { get; }
Property Value
IsFinished
Is the process finished from Start().
public bool IsFinished { get; }
Property Value
IsLocked
Is started but not finished. IsLocked keeps true even if Pause() is called. The property is true if a task started and the task has not yet finished.
public bool IsLocked { get; }
Property Value
IsOnPlayerThread
True when the calling thread is the live player task's thread — the thread that runs MainAction (or a one-shot) and drives the queued jobs. False on every other thread and when no task is live. A host uses it to tell a mission's own call (on the player thread, paced) from a request that arrives while the player is locked.
public bool IsOnPlayerThread { get; }
Property Value
IsRunning
Is running. Not paused either finished. The property is true if a task started and the task has not yet finished and Pause() is not called.
public bool IsRunning { get; }
Property Value
Logger
Logger for reporting exceptions during task execution.
public ILogger Logger { get; }
Property Value
MainAction
Gets or sets the main action to be executed by the player.
public Action<PacePlayee> MainAction { get; set; }
Property Value
PauseCheckedHook
Internal Use Only. A test's seam in Pause(): called on the pausing thread between its check (the run live, the gate open) and its closing of the gate, under the tail gate, so the run's end – and every other Pause, Resume or Terminate – waits for it. Null in production. A callback signals and waits, bounded; it never calls into the player (a Terminate from it would wait on the gate it holds) and never waits for the run's end, which waits for it.
public Action PauseCheckedHook { get; set; }
Property Value
ResetReleasedReadLockHook
Internal Use Only. A test's seam in Reset(): called on the resetting thread after the reset released the read lock and before it releases the reset lock – a launch is admitted from here on while the reset still holds its lock. Null in production. A callback signals and waits, bounded; it never calls into the player.
public Action ResetReleasedReadLockHook { get; set; }
Property Value
ResumeCheckedHook
Internal Use Only. A test's seam in Resume(): called on the resuming thread between its check (the run live, the gate closed) and its opening of the gate, under the tail gate; the rules of PauseCheckedHook. Null in production.
public Action ResumeCheckedHook { get; set; }
Property Value
RunCount
Monotonically increasing count of runs started by Start(),
incremented synchronously before the run's task launches (never reset).
Serves as the run identity for status pollers: pairing this with
IsFinished in one snapshot lets a client distinguish
“the run I started has finished” from a stale Finished left by a
previous run. 0 until the first run starts.
A one-shot launched by TryStartOneShot(Action<PacePlayee>) is not a run and does not count.
public long RunCount { get; }
Property Value
RunTailPublishedHook
Internal Use Only. A test's seam in the run's tail: called on the player thread once the run's end is published – IsLocked false, a Start() admitted from here on – and before the closing events (IsRunningChangedEvent false, IsLockedChangedEvent false). Null in production. A callback signals and waits, bounded; it never calls into the player: a Wait(), a Reset() or a Dispose() there runs inside the very task they would wait for.
public Action RunTailPublishedHook { get; set; }
Property Value
TerminateCheckedHook
Internal Use Only. A test's seam in Terminate(): called on the stopping thread between its check (the run live, not yet cancelled) and the cancel, under the tail gate; the rules of PauseCheckedHook. Null in production.
public Action TerminateCheckedHook { get; set; }
Property Value
TerminateFoundNoRunHook
Internal Use Only. A test's seam in Terminate(): called on the stopping thread after its check found no run (or one-shot) live and the player finished, before the stop drops the finished flag – outside every lock, so a launch is admitted meanwhile. Null in production. A callback signals and waits, bounded; it never calls into the player.
public Action TerminateFoundNoRunHook { get; set; }
Property Value
Methods
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
Dispose(bool)
Releases the unmanaged resources and optionally releases the managed resources.
protected virtual void Dispose(bool disposing)
Parameters
disposingbooltrue to release both managed and unmanaged resources; false to release only unmanaged resources.
Pause()
Pauses the execution. Nothing on an idle player, and nothing once the run's end is published: the check and the closing of the gate are one step under the tail gate against the run's end, and the end waits for the running-state event's delivery, so a Pause that saw the run live cannot close the gate after the tail reopened it, nor report a later run paused with a late false. No run lock: the run's own pause – the session's at a collision or a stroke-limit hit, the sentence step's, a licence pause – runs on the player thread, and a step-built handler's on the pipeline's pump; a new read lock there queued behind a Start() waiting on the write lock while a Reset() held the read lock waiting for this very run (through the pipeline's drain, for the pump), and the three waited on each other for good. Against a launch the tail gate suffices: a Pause that reads no task does nothing, and one that reads the new task pauses it, as a Pause right after the launch would.
public void Pause()
Reset()
Resets the player to its initial state: stops the run and waits for it (closing events included), clears IsFinished, then raises ResetedEvent. One reset at a time (the reset lock): a second reset waits until the first one's handlers have returned, then runs its own. A reset from inside the last launched task – a handler of its closing events, while no launch has been admitted in its tail (after one, the handler's reset is an ordinary one, which stops that launch) – while another reset holds the lock and has not finished returns at once and leaves the reset to that one: that one waits for this very task (or will), and resets once the task is over; waiting for the lock here, the two would wait on each other for good. One that has finished – its handlers returned before this task was launched – did not reset it: the call waits for it, then resets. From the player thread it cancels its own run and runs the handlers under it (Wait() returns at once from inside the task), which is no reset of that run: the host refuses a reset on the player thread, and on a thread the run waits on, before calling.
public void Reset()
Resume()
Resumes the execution if paused. Nothing on an idle player, and nothing once the run's end is published: the check and the opening of the gate are one step under the tail gate against the run's end, and the end waits for the running-state event's delivery, so a Resume that saw a paused run live cannot report the player running after its tail published the end (an idle player shown running, the host's rendering throttle left on until the next run). No run lock, for the deadlock Pause() describes.
public void Resume()
RunToNextPace()
Runs to the next pace point. Only a pace point on the player thread takes the one-step request — a Pace() another thread waits in never does — and the request ends with the run: one that no pace point took (a run with no pace point, a request after the run's last one) does not carry over.
public void RunToNextPace()
Start()
Starts the main action execution. The check and the launch happen under the write lock, so of two callers racing for the same player exactly one launches the run; the other learns it from the return value, the only place the outcome shows — IsLocked read before or after the call cannot tell whose run is live.
public bool Start()
Returns
- bool
True if this call launched the run (RunCount moved up and the run's task is live); false if nothing was done because a run is already live or the player is disposed, or, from inside the last launched task (a handler of its closing events), because a Reset() waits for that task.
Terminate()
Terminates the execution by cancelling the task. The check and the cancel are one step under the tail gate
against the run's end, so a run that has ended is never cancelled (its token's registrations would run for a
run that is over) and a stop that found the run live never meets a source the run's end took away. With no run
live, or the run already cancelled, a finished player goes back to not finished. No run lock, as for
Pause(): a stop from the run's own thread or from a running-state handler, while a Reset()
holds the read lock waiting for the run and a Start() waits on the write lock, queued behind the
Start for good. Against a launch none is needed: the token source and the task are written under the write lock
before the task starts, and a stop that reads no task, or no source, does nothing to the run, as one before the
launch would. On an idle finished player the stop drops the flag – the host arms its reset-after-stop on that
event (HiMech MachiningSession) – under the run lock's read side taken without waiting, after a second
look: a launch then waits for the event's handlers, so the next run starts as after a stop; one that holds or waits
for the write lock wins, and the stop leaves its run alone, finished flag included, as a stop during the launch.
public void Terminate()
TryRunAtPacePoint(IPacedJob, CancellationToken, out Task, bool)
Queues job to run on the player thread from its next pace point — the next
Pace() of the live run, the one it is parked in when paused (the parked
thread is woken for it), or the end of the run's action if no pace point comes first — and hands
back the task that completes when the job has run. Nothing runs concurrently with the run: the
player thread drives the job itself, chunk by chunk, and the run does not advance until the job
has reported its last chunk. Every chunk boundary is a pace point of the run: a Pause()
parks the job there, Resume() lets it go on, and a stop (Terminate(),
Reset()) opens the gate so the next chunk sees the run's CancellationToken
— a job that observes the token finishes early; one that throws
OperationCanceledException ends as cancelled. Queued jobs run in order, one at a time;
a pace point reached inside a chunk parks on the gate but never runs the next job.
With resumeIfPaused, a job taken while the run is paused — the run parked at
the pace point when the job is queued, or paused before the queued job's pace point came — first
resumes the run through Resume() (IsRunningChangedEvent fires,
IsRunning is true while the job computes, and a Pause meanwhile parks it at the next
chunk boundary like any play), then pauses it again through Pause() after the last
chunk, before its task completes and before the run could take a step: the run ends up where and
how it was. A Resume pressed by the user meanwhile does not cancel that restore; a stop meanwhile
ends the run instead and nothing is restored. A job taken while the run plays neither resumes nor
pauses anything. The auto-resume opens the pass gate for every thread: a play driven on another
thread through the same playee goes on while the job computes — a host that lets a second play
exist must keep it off the session itself.
Returns false without queuing anything when no run is live (use TryStartOneShot(Action<PacePlayee>))
or the player is disposed. While the job is still queued, cancelling
cancellationToken withdraws it (it never runs and completion
is cancelled); once it has started the token has no effect. A run that stops before the pace point
cancels the queued jobs instead of running them. The task faults with the job's exception, is
cancelled when a chunk throws OperationCanceledException, and completes otherwise;
when the player itself fails between two chunks, or on the auto-resume before the first one (a
running-state handler that throws), the job is told first (Abandon(Exception)) and
the task faults with that exception, whatever its type. The run itself is unaffected by any of
these.
public bool TryRunAtPacePoint(IPacedJob job, CancellationToken cancellationToken, out Task completion, bool resumeIfPaused = false)
Parameters
jobIPacedJobThe job to drive on the player thread; each chunk receives the playee of the live run.
cancellationTokenCancellationTokenWithdraws the job while it is still queued.
completionTaskThe task that tracks the job; null when the call returns false.
resumeIfPausedboolResume a paused run for the job and pause it again after the job.
Returns
- bool
True if the job was queued; false if no run is live or the player is disposed.
TryRunAtPacePoint(Action<PacePlayee>, CancellationToken, out Task)
Queues action as a job of one chunk; see
TryRunAtPacePoint(IPacedJob, CancellationToken, out Task, bool). A pace point
inside the action parks while the player is paused (it is the action's own chunk) and
never runs the queue.
public bool TryRunAtPacePoint(Action<PacePlayee> action, CancellationToken cancellationToken, out Task completion)
Parameters
actionAction<PacePlayee>The action to run on the player thread; it receives the playee of the live run.
cancellationTokenCancellationTokenWithdraws the action while it is still queued.
completionTaskThe task that tracks the action; null when the call returns false.
Returns
- bool
True if the action was queued; false if no run is live or the player is disposed.
TryStartOneShot(Action<PacePlayee>)
Runs action on the player task in place of MainAction — the same locking,
cancellation token, IsLocked/IsRunning events and Reset semantics as a normal run, but it neither counts as a run
(RunCount unchanged) nor marks the player finished. Returns false without doing anything when a run
is live or the player is disposed (a live run takes work through
TryRunAtPacePoint(IPacedJob, CancellationToken, out Task, bool) instead). The
check and the launch happen under the write lock, so a Start racing this call can neither swallow nor hijack the
action.
public bool TryStartOneShot(Action<PacePlayee> action)
Parameters
actionAction<PacePlayee>The action to run on the player task; it receives the PacePlayee of the one-shot.
Returns
- bool
True if the one-shot was launched; false if a run is live or the player is disposed.
TryStartOneShot(Action<PacePlayee>, out Task)
TryStartOneShot(Action<PacePlayee>), handing back the one-shot's task: it completes once the one-shot has ended, its closing events included, whether its action ran or not (a handler of the lock event that throws ends the one-shot ahead of the action). A caller that waits for what its action does waits for this too, so that a one-shot ended without its action answers; an unlock event cannot tell it, since the unlock of the run before can come while this one-shot is live, and this one's own can come with the player locked again by a launch admitted in its tail. The task faults only when a handler of the closing events threw (logged by the player).
public bool TryStartOneShot(Action<PacePlayee> action, out Task oneShot)
Parameters
actionAction<PacePlayee>The action to run on the player task; it receives the PacePlayee of the one-shot.
oneShotTaskThe one-shot's task; null when the call returns false.
Returns
- bool
True if the one-shot was launched; false if a run is live or the player is disposed.
Wait()
Waits for the task to complete: the live run's (or one-shot's), its closing events included, or the last one's, which has. From inside that task – a closing-event handler's Reset() or Dispose(), while no launch has been admitted in the run's tail – it returns at once: the wait would be for itself. After such a launch the last task is the new one, and a closing-event handler of the run before waits for it. A handler that threw in the run's tail faulted the task; that fault is the logger's (Logger, through the launch), not the waiter's.
public void Wait()
Events
IsFinishedChangedEvent
Event triggered when the IsFinished state changes.
public event Action<bool> IsFinishedChangedEvent
Event Type
IsLockedChangedEvent
Event triggered when the lock state changes. The false of a run's end is raised inside the run's own task, after its end is published. While no launch has been admitted in that tail the task is the last launched one: a handler's Wait() returns at once, its Reset() leaves the reset to another one that waits for the run, if any, and its launch (Start(), TryStartOneShot(Action<PacePlayee>), RunToNextPace()) answers false, nothing launched, while such a reset waits (see Reset(), Start()). Once a launch has been admitted there, the false is late: the handler's Wait waits for that launch, its Reset is an ordinary reset, which stops it, and its launch finds a run live.
public event Action<bool> IsLockedChangedEvent
Event Type
IsRunningChangedEvent
Event triggered when the running state changes. The run's end waits for the handlers of a Pause() or a Resume() still on their way, so a resume's true reaches them before the end's false: a handler returns without waiting for the run's end – no Reset(), Dispose() or Wait() in it, and no launch (Start(), TryStartOneShot(Action<PacePlayee>), a RunToNextPace() on an idle player), whose write lock waits for a reset that waits for the run. A Pause(), Resume() or Terminate() in it is fine (none takes the run lock), and so is a wait for the step pipeline: the handlers run outside the player's tail gate. The false of a run's end is raised after the end is published, and a run admitted in that tail may have raised its own true already: a handler that follows the player's state reads it (IsRunning) rather than the argument, and a Pause or Terminate in it acts on that run.
public event Action<bool> IsRunningChangedEvent
Event Type
ResetedEvent
Event triggered after the player has been reset.
public event Action ResetedEvent