Reclaim drain owns its work: invalidation only bumps a generation, and a generation that moved aborts the tick's commit and its release.
This commit is contained in:
@@ -32,14 +32,12 @@ namespace {
|
||||
// COMPARE-ONLY and never dereferenced beyond the ValidatePtr2 the tick already makes —
|
||||
// a tab switch has to be detectable without following a pointer that may name a closed
|
||||
// project.
|
||||
std::vector<ReclaimItem> g_items;
|
||||
ReaProject* g_project = nullptr;
|
||||
ParkClock g_clock;
|
||||
FxReclaimQueue g_queue;
|
||||
ReaProject* g_project = nullptr;
|
||||
|
||||
// TrackFX_SetOffline is ASSUMED to pump the message loop, so a plugin load can re-enter
|
||||
// the timer and land back here mid-drain. Fail closed: a re-entered drain would walk the
|
||||
// same queue the outer one holds a reference into, re-running the very item that outer
|
||||
// step is still writing.
|
||||
// the timer and land back here mid-drain. Fail closed: a re-entered drain would derive a
|
||||
// second queue for the same project and race the outer one's writes track for track.
|
||||
bool g_draining = false;
|
||||
struct DrainLatch {
|
||||
DrainLatch() { g_draining = true; }
|
||||
@@ -112,13 +110,15 @@ struct ReclaimHost {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reached only after the drain has unwound and the queue has proved its generation
|
||||
// did not move, so `model` is still the object the queue was derived against.
|
||||
void releaseFx(const std::string& guid) { model->releaseSnapshotFx(guid); }
|
||||
};
|
||||
|
||||
// Every path that stops scheduling ticks leaves the same state behind: no queue, no pin,
|
||||
// nothing pending. The debounce clock is NOT part of it — see fx_reclaim.h.
|
||||
void goIdle() {
|
||||
g_items.clear();
|
||||
g_queue.discard();
|
||||
g_project = nullptr;
|
||||
g_fxReclaimPending = false;
|
||||
}
|
||||
@@ -128,10 +128,15 @@ void goIdle() {
|
||||
bool g_fxReclaimPending = false;
|
||||
|
||||
void invalidateFxReclaim() {
|
||||
goIdle();
|
||||
// A generation bump and a flag, and deliberately nothing more. Discarding the queue
|
||||
// here is what let a re-entered apply free the element the outer drain was writing
|
||||
// through, and clear the very wakeup this call had just raised.
|
||||
g_queue.invalidate();
|
||||
g_fxReclaimPending = true;
|
||||
}
|
||||
|
||||
bool fxReclaimDrainInProgress() { return g_draining; }
|
||||
|
||||
void fxReclaimTick(ViewModeModel& model, ReaProject* proj) {
|
||||
if (g_draining) return;
|
||||
// The model is mid-rewrite and its records are being consumed out from under any
|
||||
@@ -148,20 +153,21 @@ void fxReclaimTick(ViewModeModel& model, ReaProject* proj) {
|
||||
ReaProject* const project = proj ? proj : EnumProjects(-1, nullptr, 0);
|
||||
if (!ValidatePtr2(nullptr, project, "ReaProject*") || (g_project && g_project != project)) {
|
||||
goIdle();
|
||||
g_clock.forget();
|
||||
g_queue.forgetHolds();
|
||||
return;
|
||||
}
|
||||
|
||||
// DERIVED, never persisted: recomputed from view state every time the queue was
|
||||
// discarded, so a REAPER quit mid-drain simply re-derives and finishes on reopen.
|
||||
if (!g_project) {
|
||||
// invalidated, so a REAPER quit mid-drain simply re-derives and finishes on reopen.
|
||||
if (g_queue.stale()) {
|
||||
g_project = project;
|
||||
g_items = makeQueue(
|
||||
view::desiredFxState(model, model.activeModeId(), liveTrackGuids(project)));
|
||||
g_queue.adopt(makeQueue(
|
||||
view::desiredFxState(model, model.activeModeId(), liveTrackGuids(project))));
|
||||
}
|
||||
|
||||
ReclaimHost host{project, &model};
|
||||
if (drainTick(g_items, g_clock, host).settled) goIdle();
|
||||
const ReclaimRunResult run = g_queue.run(host);
|
||||
if (!run.aborted && run.settled) goIdle();
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
Reference in New Issue
Block a user