bake: name the five ways the extension can fail to answer, and move the landing verdict into a pure, tab-provable classifier
A no-answer stays a failure; it now says whether the extension never ran the landing, answered a stale generation, spoke a wire this build cannot read, cleared the request, or refused it.
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
#include "shell/capture/bake_land.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -53,13 +52,6 @@ using wire::BakeOutcome;
|
||||
using wire::BakeRequest;
|
||||
using wire::BakeStatus;
|
||||
|
||||
// The whole chain is a call and a return inside ONE editor tick, so a request older than
|
||||
// this has no reader left: it is a crash leftover, and it is CLEARED rather than landed.
|
||||
// Without the guard a stranded request would be banked on the next unrelated instance's
|
||||
// click, and the outcome written back to a key nobody will collect would persist into the
|
||||
// .rpp forever.
|
||||
constexpr std::int64_t kMaxRequestAgeSeconds = 30;
|
||||
|
||||
BakeOutcome refuse(BakeStatus status, std::string message, std::int64_t generation) {
|
||||
BakeOutcome out;
|
||||
out.status = status;
|
||||
@@ -268,15 +260,12 @@ struct Answer {
|
||||
} // namespace
|
||||
|
||||
void RunResampleBake(ReaSamplerSession& session) {
|
||||
// Three projects have to agree before anything may land: the tab the request was found
|
||||
// in, the tab whose book/ledger poll() last loaded, and the tab saveToActiveProject
|
||||
// will persist into. Land on a disagreement and one tab's bake is written into
|
||||
// another's bank. Whether REAPER makes Main_OnCommandEx's `proj` current for the
|
||||
// action's duration is unverified in the DAW; this holds either way, and a request it
|
||||
// cannot land is told why rather than silently ignored.
|
||||
// Whether REAPER makes Main_OnCommandEx's `proj` current for the action's duration is
|
||||
// unverified in the DAW; the per-key verdict holds either way, and a request it cannot
|
||||
// land is told why rather than silently ignored.
|
||||
const void* loaded = session.loadedProject();
|
||||
const void* active = EnumProjects(-1, nullptr, 0);
|
||||
const bool sessionUsable = loaded != nullptr && loaded == active;
|
||||
const wire::BakeScanContext scanContext{loaded != nullptr, loaded == active};
|
||||
const std::int64_t nowSec = static_cast<std::int64_t>(std::time(nullptr));
|
||||
|
||||
std::vector<Answer> answers;
|
||||
@@ -285,21 +274,23 @@ void RunResampleBake(ReaSamplerSession& session) {
|
||||
for (const OpenProject& open : openProjects()) {
|
||||
for (const std::string& key : pendingBakeKeys(open.proj)) {
|
||||
const std::optional<std::string> raw = readKey(open.proj, key);
|
||||
if (!raw) continue;
|
||||
const std::optional<BakeRequest> request = wire::decodeBakeRequest(*raw);
|
||||
// Not a request: an outcome this instance has not yet collected, or a value from
|
||||
// a build we do not read. Leave it — the writing instance owns clearing its key.
|
||||
if (!request) continue;
|
||||
const std::optional<BakeRequest> request =
|
||||
raw ? wire::decodeBakeRequest(*raw) : std::nullopt;
|
||||
const wire::BakeScanKey scanKey{
|
||||
request.has_value(), request ? request->generation : 0,
|
||||
static_cast<const void*>(open.proj) == loaded};
|
||||
const wire::BakeScanVerdict verdict =
|
||||
wire::classifyBakeScan(scanContext, scanKey, nowSec);
|
||||
|
||||
// Stale (either direction, so a clock moved backwards is caught too): clear the
|
||||
// key, never answer it. The instance that could read an answer is gone.
|
||||
if (std::llabs(nowSec - request->generation) > kMaxRequestAgeSeconds) {
|
||||
// Leave it — the writing instance owns clearing its own key.
|
||||
if (verdict == wire::BakeScanVerdict::Ignore) continue;
|
||||
if (verdict == wire::BakeScanVerdict::ClearStale) {
|
||||
answers.push_back(Answer{open.proj, key, std::string{}, std::string{}});
|
||||
continue;
|
||||
}
|
||||
|
||||
BakeOutcome outcome;
|
||||
if (!sessionUsable || static_cast<const void*>(open.proj) != loaded) {
|
||||
if (verdict == wire::BakeScanVerdict::RefuseWrongProject) {
|
||||
outcome = refuse(BakeStatus::WrongProject,
|
||||
"this bake's project tab is not the one the extension has "
|
||||
"loaded -- focus that tab and try again",
|
||||
@@ -310,9 +301,9 @@ void RunResampleBake(ReaSamplerSession& session) {
|
||||
}
|
||||
// A WrongProject request left sitting in a tab this call did not come from
|
||||
// prints again on every OTHER tab's bake, since the scan revisits every open
|
||||
// project each time. `active` is this call's own proxy for "the invoking tab"
|
||||
// (see the three-projects comment above) — only that tab's own refusal is fresh
|
||||
// feedback to a user who just clicked bake; every other one is a rescan repeat.
|
||||
// project each time. `active` is this call's own proxy for "the invoking tab" —
|
||||
// only that tab's own refusal is fresh feedback to a user who just clicked
|
||||
// bake; every other one is a rescan repeat.
|
||||
const bool ownRequest = static_cast<const void*>(open.proj) == active;
|
||||
std::string console;
|
||||
if (outcome.status != BakeStatus::Ok &&
|
||||
|
||||
Reference in New Issue
Block a user