Skip to content

Commit 867c632

Browse files
committed
Fix race condition in functions discovery shutdown
Attach event handlers to subprocesses before yielding to calling code to ensure that promises of shutting down discovery processes are fulfilled.
1 parent fc89a0d commit 867c632

File tree

2 files changed

+21
-11
lines changed

2 files changed

+21
-11
lines changed

src/deploy/functions/runtimes/node/index.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,24 +265,28 @@ export class Delegate {
265265
): Promise<() => Promise<void>> {
266266
const childProcess = this.spawnFunctionsProcess(config, { ...envs, PORT: port });
267267

268+
const pExit = new Promise<void>((resolve, reject) => {
269+
childProcess.once("exit", resolve);
270+
childProcess.once("error", reject);
271+
});
272+
268273
// TODO: Refactor return type to () => Promise<void> to simplify nested promises
269274
return Promise.resolve(async () => {
270-
const p = new Promise<void>((resolve, reject) => {
271-
childProcess.once("exit", resolve);
272-
childProcess.once("error", reject);
273-
});
274-
275275
try {
276276
await fetch(`http://localhost:${port}/__/quitquitquit`);
277277
} catch (e) {
278278
logger.debug("Failed to call quitquitquit. This often means the server failed to start", e);
279279
}
280-
setTimeout(() => {
280+
281+
const quitTimeout = setTimeout(() => {
281282
if (!childProcess.killed) {
282283
childProcess.kill("SIGKILL");
283284
}
284285
}, 10_000);
285-
return p;
286+
287+
return pExit.finally(() => {
288+
clearTimeout(quitTimeout);
289+
});
286290
});
287291
}
288292

src/deploy/functions/runtimes/python/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,21 +167,27 @@ export class Delegate implements runtimes.RuntimeDelegate {
167167
childProcess.stderr?.on("data", (chunk: Buffer) => {
168168
logger.error(chunk.toString("utf8"));
169169
});
170+
171+
const pExit = new Promise<void>((resolve, reject) => {
172+
childProcess.once("exit", resolve);
173+
childProcess.once("error", reject);
174+
});
175+
170176
return Promise.resolve(async () => {
171177
try {
172178
await fetch(`http://127.0.0.1:${port}/__/quitquitquit`);
173179
} catch (e) {
174180
logger.debug("Failed to call quitquitquit. This often means the server failed to start", e);
175181
}
182+
176183
const quitTimeout = setTimeout(() => {
177184
if (!childProcess.killed) {
178185
childProcess.kill("SIGKILL");
179186
}
180187
}, 10_000);
181-
clearTimeout(quitTimeout);
182-
return new Promise<void>((resolve, reject) => {
183-
childProcess.once("exit", resolve);
184-
childProcess.once("error", reject);
188+
189+
return pExit.finally(() => {
190+
clearTimeout(quitTimeout);
185191
});
186192
});
187193
}

0 commit comments

Comments
 (0)