Skip to content

Commit cd011bc

Browse files
endolinbotendolinbot
authored andcommitted
fix(familiar): clear daemon-control timeout on settle
`runDaemonControl` had a 30s timer that was never cleared when the child exited normally. After a successful Restart/Purge Daemon the timer kept the Electron event loop alive for the remainder of the 30s window, firing a no-op `child.kill()` and a `reject()` on an already-settled promise. Hoist a single-settle latch around `resolve` / `reject` that clears the timer in all three exit paths (`close`, `error`, timeout) and guards against the double-settle case where `spawn` synthesizes both an `error` event and a `close` event on ENOENT. No behavior change on the success path; a follow-up Restart no longer waits ~30s of stranded-timer event-loop time before the app can quit.
1 parent 13b791a commit cd011bc

1 file changed

Lines changed: 22 additions & 6 deletions

File tree

packages/familiar/src/daemon-manager.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// @ts-check
2-
/* global process, setTimeout */
2+
/* global process, setTimeout, clearTimeout */
33

44
/**
55
* Daemon lifecycle management.
@@ -98,26 +98,42 @@ const makeDaemonManager = logger => {
9898
},
9999
);
100100

101+
/** @type {NodeJS.Timeout | undefined} */
102+
let timer;
103+
let settled = false;
104+
const settleResolve = () => {
105+
if (settled) return;
106+
settled = true;
107+
if (timer !== undefined) clearTimeout(timer);
108+
resolve(undefined);
109+
};
110+
const settleReject = (/** @type {Error} */ err) => {
111+
if (settled) return;
112+
settled = true;
113+
if (timer !== undefined) clearTimeout(timer);
114+
reject(err);
115+
};
116+
101117
let stderr = '';
102118
child.stderr.on('data', data => {
103119
stderr += data.toString();
104120
});
105121

106122
child.on('close', code => {
107123
if (code === 0) {
108-
resolve(undefined);
124+
settleResolve();
109125
return;
110126
}
111-
reject(
127+
settleReject(
112128
new Error(`daemon-control ${verb} failed (code ${code}): ${stderr}`),
113129
);
114130
});
115131

116-
child.on('error', reject);
132+
child.on('error', err => settleReject(err));
117133

118-
setTimeout(() => {
134+
timer = setTimeout(() => {
119135
child.kill();
120-
reject(new Error(`Timeout running daemon-control ${verb}`));
136+
settleReject(new Error(`Timeout running daemon-control ${verb}`));
121137
}, 30_000);
122138
});
123139
};

0 commit comments

Comments
 (0)