Guard poll() against a mode-apply mid-flight; two doc corrections
Extends the OnTimer guard to skip session.poll() itself while modeApplyInProgress(), preventing an undo/redo or project-switch reload from replacing the view model under an in-flight applyMode.
This commit is contained in:
+1
-1
@@ -392,7 +392,7 @@ cross-reference.
|
|||||||
| **Ω-F5** | **The mode-switch symptom is both entry surfaces**, and it is *"nothing for 2 seconds, then everything switches"* — nothing renders until the whole synchronous body completes | Ω-W1-T1, Ω-W1-T2 |
|
| **Ω-F5** | **The mode-switch symptom is both entry surfaces**, and it is *"nothing for 2 seconds, then everything switches"* — nothing renders until the whole synchronous body completes | Ω-W1-T1, Ω-W1-T2 |
|
||||||
| **Ω-F6** | **ACCEPTED, and it is a spec amendment: separate the FX parking's UI half from its processing half.** Visibility / routing / FX-enable apply **synchronously**; per-FX offline/online **defers to a later idle tick** — *"the switch doesn't happen during playback anyway… the UI is the priority when toggling modes"* | Ω-W1-T1 |
|
| **Ω-F6** | **ACCEPTED, and it is a spec amendment: separate the FX parking's UI half from its processing half.** Visibility / routing / FX-enable apply **synchronously**; per-FX offline/online **defers to a later idle tick** — *"the switch doesn't happen during playback anyway… the UI is the priority when toggling modes"* | Ω-W1-T1 |
|
||||||
|
|
||||||
**SUPERSEDED — Ω-F6 and every deferred-park instruction under Ω-W1-T1 below (the new park-queue TU, its `OnTimer` drain, its `target_sources` line).** The deferral cost a SECOND undo point and could not be made to cost one: REAPER's undo surface has no append-to-point and no block held across a return to the message loop, so per-FX offline/online now runs inline in `applyMode`'s own block and the queue is deleted. The hitch is back on the switch's synchronous path, deliberately. The rest of Ω-W1-T1 (the honest undo mask, compare-before-write, `PreventUIRefresh`, the `resolve` hash map) stands as written.
|
**SUPERSEDED — Ω-F6 and every deferred-park instruction under Ω-W1-T1 below (the new park-queue TU, its `OnTimer` drain, its `target_sources` line).** The deferral cost a SECOND undo point and could not be made to cost one: REAPER's undo surface has no append-to-point and no block held across a return to the message loop, so per-FX offline/online now runs inline in `applyMode`'s own block and the queue is deleted. The hitch is back on the switch's synchronous path, deliberately. The rest of Ω-W1-T1 (the honest undo mask, compare-before-write, `PreventUIRefresh`, the `resolve` hash map) stands as written. Two of that track's acceptance criteria below are superseded with it: "paints its new state before the park work runs" assumed the split that no longer exists — a switch now parks synchronously before anything paints — and "two rapid switches leave every track in the state the SECOND switch specifies" assumed a queue that could apply both — the synchronous latch REFUSES a switch fired while another is already applying, so a rapid second switch is dropped, not applied.
|
||||||
|
|
||||||
**Verified by Daniel, not a fork:** the deck knob-spacing defect is in the **Trigger** face.
|
**Verified by Daniel, not a fork:** the deck knob-spacing defect is in the **Trigger** face.
|
||||||
**Gate is correct and must stay pixel-identical** — an acceptance criterion of Ω-W1-T4, not a
|
**Gate is correct and must stay pixel-identical** — an acceptance criterion of Ω-W1-T4, not a
|
||||||
|
|||||||
+7
-1
@@ -180,7 +180,13 @@ static void OnTimer()
|
|||||||
// the idle fast-path is a SINGLE POINTER TEST — drive only when a capture is live.
|
// the idle fast-path is a SINGLE POINTER TEST — drive only when a capture is live.
|
||||||
if (capture::g_rtCapture) capture::DriveRealtimeCapture(g_session);
|
if (capture::g_rtCapture) capture::DriveRealtimeCapture(g_session);
|
||||||
|
|
||||||
g_session.poll();
|
// poll()'s undo/redo-reload and project-switch Load branches call
|
||||||
|
// loadFromProject, replacing g_session.view() wholesale — deferred here while
|
||||||
|
// an applyMode is on the stack (reachable if a pumped message loop re-enters
|
||||||
|
// this timer mid-apply), same retry-next-tick shape as the consume guard below.
|
||||||
|
// Deferred WHOLE, not just the reload branches: poll()'s identity tracking is
|
||||||
|
// cheap to skip for one tick and resumes correctly, unlatched, on the next.
|
||||||
|
if (!reasampler::modeApplyInProgress()) g_session.poll();
|
||||||
|
|
||||||
// persist stays MODEL-ONLY (loads the saved view model but does not apply
|
// persist stays MODEL-ONLY (loads the saved view model but does not apply
|
||||||
// visibility, to avoid coupling persist to the view shell); poll() raises a
|
// visibility, to avoid coupling persist to the view shell); poll() raises a
|
||||||
|
|||||||
@@ -180,7 +180,9 @@ applies the resulting lane state to live tracks.
|
|||||||
pump premise below, and `applyMode` fails closed when re-entered
|
pump premise below, and `applyMode` fails closed when re-entered
|
||||||
(`modeApplyInProgress`). The window that remains is what a call REACHABLE FROM
|
(`modeApplyInProgress`). The window that remains is what a call REACHABLE FROM
|
||||||
a pumped message loop but not routed through `applyMode` could see — no save,
|
a pumped message loop but not routed through `applyMode` could see — no save,
|
||||||
no reload and no discard reaches it, and there is no queued intent for an undo
|
no reload and no discard reaches it (`main.cpp`'s `OnTimer` defers
|
||||||
|
`session.poll()` itself, the one caller of `loadFromProject` a re-entered timer
|
||||||
|
could reach, under this same latch), and there is no queued intent for an undo
|
||||||
to drop. What survives:
|
to drop. What survives:
|
||||||
- A `view_state` that PARSED but carries no snapshot for the track — a snapshot
|
- A `view_state` that PARSED but carries no snapshot for the track — a snapshot
|
||||||
`reconcile` pruned while its track was out of the live enumeration, then
|
`reconcile` pruned while its track was out of the live enumeration, then
|
||||||
@@ -256,7 +258,10 @@ applies the resulting lane state to live tracks.
|
|||||||
nothing to fall back on is `main.cpp`'s load glue — it spends a one-shot signal
|
nothing to fall back on is `main.cpp`'s load glue — it spends a one-shot signal
|
||||||
— so it tests `modeApplyInProgress` before consuming and retries next tick. The
|
— so it tests `modeApplyInProgress` before consuming and retries next tick. The
|
||||||
action callers just no-op, silently (`reportModeSwitchRefused` speaks only for
|
action callers just no-op, silently (`reportModeSwitchRefused` speaks only for
|
||||||
the transport gate); the user re-fires.
|
the transport gate); the user re-fires. `render_in_place`'s own `applyMode` call
|
||||||
|
is the exception to that recovery path, not to the drop itself: its refusal
|
||||||
|
leaves the membership tags it already wrote in place, so the next reapply — not
|
||||||
|
a user re-fire of render-in-place — is what brings the tracks into sync.
|
||||||
- `fx_offline`'s identity keying (`TrackFX_GetFXGUID`) assumes the GUID stays
|
- `fx_offline`'s identity keying (`TrackFX_GetFXGUID`) assumes the GUID stays
|
||||||
attached to its plugin across a chain mutation while parked. That is
|
attached to its plugin across a chain mutation while parked. That is
|
||||||
`[verify — DAW]` (see `fxGuidString` in `view_fx_park.cpp`) and SWS issue #802 is a
|
`[verify — DAW]` (see `fxGuidString` in `view_fx_park.cpp`) and SWS issue #802 is a
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ std::vector<LaneTrack> readLaneTracks(
|
|||||||
std::vector<LaneTrack> tracks;
|
std::vector<LaneTrack> tracks;
|
||||||
tracks.reserve(handleByGuid.size());
|
tracks.reserve(handleByGuid.size());
|
||||||
for (const auto& [guid, tr] : handleByGuid) {
|
for (const auto& [guid, tr] : handleByGuid) {
|
||||||
|
if (!tr) continue; // TrackHandles legally carries nulls post-validation (view.cpp)
|
||||||
LaneTrack lt;
|
LaneTrack lt;
|
||||||
lt.trackGuid = guid;
|
lt.trackGuid = guid;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user