event dispatcher
This commit is contained in:
@@ -307,6 +307,24 @@ Suggested components:
|
||||
|
||||
Initial implementation can be single-process and mostly single-dispatch-thread. The important part is that event publication and event handling become explicit.
|
||||
|
||||
### Dispatcher Ownership Decision
|
||||
|
||||
The first concrete implementation uses one app-owned `RuntimeEventDispatcher`.
|
||||
|
||||
Ownership:
|
||||
|
||||
- `OpenGLComposite` owns the dispatcher as part of the current composition root.
|
||||
|
||||
References:
|
||||
|
||||
- `RuntimeServices` receives the dispatcher and passes it to `ControlServices`.
|
||||
- `RuntimeCoordinator` receives the dispatcher so coordinator outcomes can become explicit events.
|
||||
- `RuntimeUpdateController` receives the dispatcher so it can become the first effect/apply handler.
|
||||
|
||||
This is intentionally a composition-root dependency, not a new subsystem dependency. Subsystems should not construct their own dispatchers, and future tests should use `RuntimeEventTestHarness` rather than creating ad hoc event plumbing.
|
||||
|
||||
The dispatcher should move out of `OpenGLComposite` only if a later application-shell/composition-root object replaces `OpenGLComposite` as the owner of subsystem wiring.
|
||||
|
||||
## Queue Policy
|
||||
|
||||
Not every event deserves the same queue semantics.
|
||||
@@ -342,6 +360,63 @@ Some calls may remain synchronous during Phase 2:
|
||||
|
||||
The rule is that synchronous calls should still publish events for accepted/rejected/completed work, so the rest of the app does not need to infer side effects from the call path.
|
||||
|
||||
## Event Bridge Policy
|
||||
|
||||
This section is the implementation rulebook for converting existing direct calls and result queues into events. Future Phase 2 lanes should use this table unless they deliberately update the policy here first.
|
||||
|
||||
### Bridge Categories
|
||||
|
||||
| Bridge category | Use when | Queue shape | Handler expectation |
|
||||
| --- | --- | --- | --- |
|
||||
| `fifo-fact` | every occurrence matters and must be observed in order | bounded FIFO | handler consumes each event exactly once |
|
||||
| `coalesced-latest` | only the latest value per key matters | bounded coalescing queue | handler consumes the latest event and telemetry records collapsed count |
|
||||
| `sync-command-with-event` | caller needs an immediate success/error result | direct owner call plus follow-up event publication | handler must not be required for the caller's response |
|
||||
| `observation-only` | event is telemetry/diagnostic and must not drive core behavior | FIFO or coalesced depending on rate | handler failure must never block app behavior |
|
||||
| `compatibility-poll` | source cannot yet publish an event directly | temporary poll adapter publishes typed events | poll interval should shrink or become wakeup-driven over Phase 2 |
|
||||
|
||||
### Current Bridge Decisions
|
||||
|
||||
| Current flow | First Phase 2 bridge | Event(s) | Queue policy |
|
||||
| --- | --- | --- | --- |
|
||||
| OSC latest-value updates | `ControlServices` ingress bridge | `OscValueReceived`, optional `OscValueCoalesced` | `coalesced-latest` by route key |
|
||||
| OSC commit after settle | `ControlServices -> RuntimeCoordinator` bridge | `OscCommitRequested`, then `RuntimeMutationAccepted` or `RuntimeMutationRejected` | commit request `coalesced-latest` by route key; mutation result `fifo-fact` |
|
||||
| HTTP/UI mutation needing response | direct call into `RuntimeCoordinator` | `RuntimeMutationAccepted` or `RuntimeMutationRejected` after the synchronous response path | `sync-command-with-event` |
|
||||
| runtime-state broadcast request | presentation/broadcast bridge | `RuntimeStatePresentationChanged`, `RuntimeStateBroadcastRequested` | `coalesced-latest` by event type or reason family |
|
||||
| manual reload button | control ingress bridge | `ManualReloadRequested`, then `RuntimeReloadRequested` | `fifo-fact` for manual request; reload execution may coalesce |
|
||||
| file watcher changes | file-watch bridge | `FileChangeDetected`, then `RuntimeReloadRequested` | `coalesced-latest` by path, then coalesced reload request |
|
||||
| runtime store poll fallback | compatibility poll adapter | `ShaderPackagesChanged`, `RuntimeReloadRequested`, or warning event | `compatibility-poll` until file events fully replace polling |
|
||||
| shader build request | runtime/render bridge | `ShaderBuildRequested` | `coalesced-latest` by input dimensions and preserve-feedback flag |
|
||||
| shader build ready/failure/apply | shader build lifecycle bridge | `ShaderBuildPrepared`, `ShaderBuildFailed`, `ShaderBuildApplied`, `CompileStatusChanged` | `fifo-fact` |
|
||||
| render snapshot publication | snapshot bridge | `RenderSnapshotPublishRequested`, `RenderSnapshotPublished` | request may coalesce by output dimensions; published event is `fifo-fact` |
|
||||
| render reset request/application | render bridge | `RenderResetRequested`, `RenderResetApplied` | `fifo-fact` |
|
||||
| input signal changes | backend observation bridge | `InputSignalChanged` | `coalesced-latest` by signal lane |
|
||||
| output late/dropped/completed frames | backend timing bridge | `OutputFrameCompleted`, `OutputLateFrameDetected`, `OutputDroppedFrameDetected` | late/dropped `fifo-fact`; high-rate completed frames may become `observation-only` coalesced metrics |
|
||||
| warnings and recovery | telemetry bridge | `SubsystemWarningRaised`, `SubsystemWarningCleared`, `SubsystemRecovered` | `fifo-fact` for lifecycle transitions |
|
||||
| queue depth/timing samples | telemetry metrics bridge | `QueueDepthChanged`, `TimingSampleRecorded` | `coalesced-latest` by metric key |
|
||||
|
||||
### Bridge Rules
|
||||
|
||||
- A bridge may translate an old direct call into an owner command, but it must publish the accepted/rejected/completed event that describes the outcome.
|
||||
- A bridge must not mutate state owned by another subsystem just because it handles that subsystem's event.
|
||||
- A coalesced event must have a stable key in code and a documented policy here.
|
||||
- A FIFO event should be cheap enough that retaining every occurrence is useful. If not, turn it into a coalesced metric before putting it on a hot path.
|
||||
- A synchronous bridge must treat event publication as a side effect of the owner decision, not as the mechanism that produces the direct caller's response.
|
||||
- A compatibility poll adapter should be named as temporary in code so it does not become the new long-term coordination model.
|
||||
- Handler failure should be reported through telemetry and dispatch metrics. It should not throw back across subsystem boundaries.
|
||||
|
||||
### First Integration Recommendation
|
||||
|
||||
The safest first behavior-changing bridge is `RuntimeStateBroadcastRequested`.
|
||||
|
||||
It is low risk because:
|
||||
|
||||
- it is already a side effect of many coordinator outcomes
|
||||
- duplicate requests are naturally coalescable
|
||||
- the handler can call the existing `ControlServices::BroadcastState()` path
|
||||
- success can be verified through existing UI behavior and event tests
|
||||
|
||||
After that, the next bridge should be `ShaderBuildRequested`, because it already behaves like a queued side effect and has clear follow-up events.
|
||||
|
||||
## Target Flow Examples
|
||||
|
||||
### OSC Parameter Update
|
||||
|
||||
Reference in New Issue
Block a user