Watch
1
0
Fork
You've already forked souveraine
0

viewtop-control: redeliver a commit the socket dropped

A transport error cleared the whole queue, so a lost overview_commit left the
compositor carrying windows the shell believed it had released. 52 dropped
queues and 113 PeerClosedErrors in one boot on blueline. commit, cancel and
workspace are idempotent, so each gets one retry; progress is a level and does not.
This commit is contained in:
Fimeg 2026-08-16 10:31:18 -04:00
commit 2b4c827b66

View file

@ -224,6 +224,39 @@ Singleton {
// that was refused), and dropping the second would be silent.
property var _queue: []
property var _inflight: null
// --- Verbs that must arrive ---------------------------------------------
//
// A transport error is not the compositor refusing. It is the message not
// being delivered, and for a handful of verbs those two have completely
// different consequences: a lost `overview_commit` leaves the compositor
// still carrying windows the shell believes it released, so the app stays
// shrunk and **nothing in the system will ever put it back**. Casey,
// 2026-08-16: *"once I put a window in multitasking sometimes it glitches
// and then I have to drag it back down manually once I reopen it"*
// reopening starts a fresh carry, and it is that carry's commit that
// finally releases the stranded one.
//
// Measured the same morning on blueline, one boot, ~90 minutes of ordinary
// use: **52** transport failures, each of which executed `_queue = []`, and
// 113 `PeerClosedError`s behind them. The odds of one of those landing on a
// commit are not small, which is exactly the "sometimes".
//
// All three are idempotent committing a released carry is `Idle`, and
// going to the zone you are already on is a no-op so redelivering costs
// nothing and losing one costs the gesture. Everything else still drops:
// `overview_progress` is a level and the next one supersedes it.
//
// One retry, not a loop. A compositor that is genuinely gone must not turn
// this into a spin.
readonly property var _mustArrive: ["overview_commit", "overview_cancel", "workspace"]
function _mustBeDelivered(msg) {
if (!msg)
return false;
const name = msg.intent || msg.op;
return root._mustArrive.indexOf(name) !== -1 && (msg._tries || 0) < 1;
}
// Whether the in-flight request was answered before the socket closed.
// The compositor serves exactly one request per connection and then hangs
// up, so a disconnect is the *normal* end of every exchange and telling
@ -446,22 +479,40 @@ Singleton {
if (root._inflight === null && root._queue.length === 0)
return;
const intent = (root._inflight && root._inflight.intent)
|| (root._inflight && root._inflight.op) || "?";
const failed = root._inflight;
const intent = (failed && failed.intent) || (failed && failed.op) || "?";
root.lastError = "viewtop socket unavailable";
// Name the verb and the count. The old wording claimed the
// Keep what must arrive, drop what was only a level. See
// `_mustArrive` this used to be `_queue = []` unconditionally,
// which is how a commit went missing and a window stranded.
const keep = [];
for (const msg of [failed].concat(root._queue)) {
if (!root._mustBeDelivered(msg))
continue;
msg._tries = (msg._tries || 0) + 1;
keep.push(msg);
}
const dropped = root._queue.length + (failed ? 1 : 0) - keep.length;
// Name the verb and the counts. The old wording claimed the
// compositor could not be reached, which was the one thing it never
// established on 2026-08-13 this fired once per shell start while
// viewtop answered a hand-written request on that same socket in
// the same second. A transport error is not a diagnosis.
// the same second. A transport error is not a diagnosis, and one
// that does not say what it ate cannot be traced to the symptom it
// caused three days later.
console.error("[viewtop-control] " + intent + " was not delivered:"
+ " the socket at " + sock.path + " closed with the request in"
+ " flight" + (root._queue.length > 0
? " (" + root._queue.length + " queued behind it dropped)"
: ""));
+ " flight (" + keep.length + " redelivered, "
+ dropped + " dropped)");
root._inflight = null;
root._queue = [];
root._queue = keep;
if (keep.length === 0)
root.refused(intent, root.lastError);
else
root._pump();
}
parser: SplitParser {