@typeberry/lib 0.9.0-3f2c45b → 0.9.0-959889e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/core/utils/index.d.ts +1 -0
- package/packages/core/utils/index.d.ts.map +1 -1
- package/packages/core/utils/index.js +1 -0
- package/packages/core/utils/shutdown.d.ts +18 -0
- package/packages/core/utils/shutdown.d.ts.map +1 -0
- package/packages/core/utils/shutdown.js +80 -0
- package/packages/core/utils/shutdown.test.d.ts +2 -0
- package/packages/core/utils/shutdown.test.d.ts.map +1 -0
- package/packages/core/utils/shutdown.test.js +155 -0
- package/packages/extensions/ipc/server.d.ts.map +1 -1
- package/packages/extensions/ipc/server.js +7 -0
- package/packages/jam/node/main-fuzz.d.ts +4 -1
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +29 -14
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7C,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7C,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type Closer = () => Promise<void>;
|
|
2
|
+
export interface ShutdownLogger {
|
|
3
|
+
info: (msg: string) => void;
|
|
4
|
+
error: (msg: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface ShutdownOptions {
|
|
7
|
+
/** Max ms allowed for `close` before forced exit(1). Default 10_000. */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
/** Signals to listen for. Default ["SIGTERM", "SIGINT"]. */
|
|
10
|
+
signals?: readonly NodeJS.Signals[];
|
|
11
|
+
/** Logger; defaults to a no-op logger. */
|
|
12
|
+
log?: ShutdownLogger;
|
|
13
|
+
/** Test hook used instead of process.exit. */
|
|
14
|
+
exit?: (code: number) => never;
|
|
15
|
+
}
|
|
16
|
+
/** Returns an uninstall function (used by tests). */
|
|
17
|
+
export declare function installShutdownHandlers(close: Closer, options?: ShutdownOptions): () => void;
|
|
18
|
+
//# sourceMappingURL=shutdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/shutdown.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,0CAA0C;IAC1C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAChC;AASD,qDAAqD;AACrD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,IAAI,CAiFhG"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const DEFAULT_SIGNALS = ["SIGTERM", "SIGINT"];
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
3
|
+
function isNodeRuntime() {
|
|
4
|
+
return typeof process !== "undefined" && typeof process.on === "function" && typeof process.exit === "function";
|
|
5
|
+
}
|
|
6
|
+
/** Returns an uninstall function (used by tests). */
|
|
7
|
+
export function installShutdownHandlers(close, options = {}) {
|
|
8
|
+
if (!isNodeRuntime()) {
|
|
9
|
+
return () => { };
|
|
10
|
+
}
|
|
11
|
+
const signals = options.signals ?? DEFAULT_SIGNALS;
|
|
12
|
+
const exit = (options.exit ?? process.exit);
|
|
13
|
+
const log = options.log;
|
|
14
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15
|
+
let state = "idle";
|
|
16
|
+
const forceExit = (signal) => {
|
|
17
|
+
log?.error(`Received ${signal} during shutdown, forcing exit.`);
|
|
18
|
+
try {
|
|
19
|
+
exit(1);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// test-time exit throws; swallow.
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const handler = (signal) => {
|
|
26
|
+
if (state === "closing" || state === "done") {
|
|
27
|
+
forceExit(signal);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
state = "closing";
|
|
31
|
+
log?.info(`Received ${signal}, draining...`);
|
|
32
|
+
void (async () => {
|
|
33
|
+
const TIMED_OUT = Symbol("timed-out");
|
|
34
|
+
let timeoutHandle;
|
|
35
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
36
|
+
timeoutHandle = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
|
|
37
|
+
});
|
|
38
|
+
timeoutHandle?.unref?.();
|
|
39
|
+
let code = 0;
|
|
40
|
+
try {
|
|
41
|
+
const outcome = await Promise.race([
|
|
42
|
+
close().then(() => "ok", (e) => ({ err: e })),
|
|
43
|
+
timeoutPromise,
|
|
44
|
+
]);
|
|
45
|
+
if (outcome === TIMED_OUT) {
|
|
46
|
+
code = 1;
|
|
47
|
+
log?.error(`Shutdown cleanup exceeded ${timeoutMs}ms timeout, forcing exit.`);
|
|
48
|
+
}
|
|
49
|
+
else if (typeof outcome === "object" && "err" in outcome) {
|
|
50
|
+
code = 1;
|
|
51
|
+
const e = outcome.err;
|
|
52
|
+
log?.error(`Shutdown close threw: ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
log?.info("Shutdown complete.");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
clearTimeout(timeoutHandle);
|
|
60
|
+
state = "done";
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
exit(code);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// test-time exit throws; swallow.
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
};
|
|
70
|
+
// Node passes the signal name to the listener, so a single handler works
|
|
71
|
+
// for every signal.
|
|
72
|
+
for (const sig of signals) {
|
|
73
|
+
process.on(sig, handler);
|
|
74
|
+
}
|
|
75
|
+
return () => {
|
|
76
|
+
for (const sig of signals) {
|
|
77
|
+
process.off(sig, handler);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.test.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/shutdown.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { afterEach, describe, it } from "node:test";
|
|
3
|
+
import { installShutdownHandlers } from "./shutdown.js";
|
|
4
|
+
function makeExitRecorder() {
|
|
5
|
+
let resolveExited;
|
|
6
|
+
const exited = new Promise((resolve) => {
|
|
7
|
+
resolveExited = resolve;
|
|
8
|
+
});
|
|
9
|
+
const recorder = {
|
|
10
|
+
exitCode: null,
|
|
11
|
+
exited,
|
|
12
|
+
exit: ((code) => {
|
|
13
|
+
recorder.exitCode = code;
|
|
14
|
+
resolveExited();
|
|
15
|
+
// Satisfy `never` and abort the calling promise chain.
|
|
16
|
+
throw new Error(`__test_exit_${code}`);
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
return recorder;
|
|
20
|
+
}
|
|
21
|
+
const silentLog = { info: () => { }, error: () => { } };
|
|
22
|
+
describe("utils::installShutdownHandlers", () => {
|
|
23
|
+
const uninstallers = [];
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
while (uninstallers.length > 0) {
|
|
26
|
+
uninstallers.pop()?.();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
it("calls close and exits 0 on first SIGTERM", async () => {
|
|
30
|
+
const exitRec = makeExitRecorder();
|
|
31
|
+
let closeCalled = false;
|
|
32
|
+
uninstallers.push(installShutdownHandlers(async () => {
|
|
33
|
+
closeCalled = true;
|
|
34
|
+
}, { exit: exitRec.exit, log: silentLog }));
|
|
35
|
+
process.emit("SIGTERM", "SIGTERM");
|
|
36
|
+
await exitRec.exited;
|
|
37
|
+
assert.strictEqual(closeCalled, true);
|
|
38
|
+
assert.strictEqual(exitRec.exitCode, 0);
|
|
39
|
+
});
|
|
40
|
+
it("exits with code 1 when cleanup exceeds timeoutMs", async () => {
|
|
41
|
+
const exitRec = makeExitRecorder();
|
|
42
|
+
let closeStarted = false;
|
|
43
|
+
let errorLogged = null;
|
|
44
|
+
let releaseClose;
|
|
45
|
+
const closeReleased = new Promise((resolve) => {
|
|
46
|
+
releaseClose = resolve;
|
|
47
|
+
});
|
|
48
|
+
uninstallers.push(installShutdownHandlers(async () => {
|
|
49
|
+
closeStarted = true;
|
|
50
|
+
await closeReleased;
|
|
51
|
+
}, {
|
|
52
|
+
exit: exitRec.exit,
|
|
53
|
+
timeoutMs: 5,
|
|
54
|
+
log: {
|
|
55
|
+
info: () => { },
|
|
56
|
+
error: (msg) => {
|
|
57
|
+
errorLogged = msg;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
process.emit("SIGTERM", "SIGTERM");
|
|
62
|
+
await exitRec.exited;
|
|
63
|
+
assert.strictEqual(closeStarted, true, "close should have been invoked");
|
|
64
|
+
assert.strictEqual(exitRec.exitCode, 1);
|
|
65
|
+
assert.ok(errorLogged !== null, "expected timeout to be logged");
|
|
66
|
+
assert.match(errorLogged, /timeout|exceed/i);
|
|
67
|
+
// Allow the in-flight close promise to settle so the test doesn't leak.
|
|
68
|
+
releaseClose();
|
|
69
|
+
});
|
|
70
|
+
it("forces exit 1 when a second signal arrives during cleanup", async () => {
|
|
71
|
+
const exitRec = makeExitRecorder();
|
|
72
|
+
let releaseClose;
|
|
73
|
+
const closeReleased = new Promise((resolve) => {
|
|
74
|
+
releaseClose = resolve;
|
|
75
|
+
});
|
|
76
|
+
let errorLogged = null;
|
|
77
|
+
uninstallers.push(installShutdownHandlers(async () => {
|
|
78
|
+
await closeReleased;
|
|
79
|
+
}, {
|
|
80
|
+
// Large timeout so this test can't pass via the timeout path.
|
|
81
|
+
timeoutMs: 60_000,
|
|
82
|
+
exit: exitRec.exit,
|
|
83
|
+
log: {
|
|
84
|
+
info: () => { },
|
|
85
|
+
error: (msg) => {
|
|
86
|
+
errorLogged = msg;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}));
|
|
90
|
+
process.emit("SIGTERM", "SIGTERM");
|
|
91
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
92
|
+
process.emit("SIGINT", "SIGINT");
|
|
93
|
+
await exitRec.exited;
|
|
94
|
+
assert.strictEqual(exitRec.exitCode, 1);
|
|
95
|
+
assert.ok(errorLogged !== null, "expected force-exit message to be logged");
|
|
96
|
+
assert.match(errorLogged, /SIGINT/);
|
|
97
|
+
releaseClose();
|
|
98
|
+
});
|
|
99
|
+
it("does nothing after uninstall", async () => {
|
|
100
|
+
const exitRec = makeExitRecorder();
|
|
101
|
+
let closeCalled = false;
|
|
102
|
+
const uninstall = installShutdownHandlers(async () => {
|
|
103
|
+
closeCalled = true;
|
|
104
|
+
}, { exit: exitRec.exit, log: silentLog });
|
|
105
|
+
uninstall();
|
|
106
|
+
process.emit("SIGTERM", "SIGTERM");
|
|
107
|
+
// Give any (incorrectly registered) handler a tick to fire.
|
|
108
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
109
|
+
assert.strictEqual(closeCalled, false);
|
|
110
|
+
assert.strictEqual(exitRec.exitCode, null);
|
|
111
|
+
});
|
|
112
|
+
it("is a no-op in non-Node runtimes", async () => {
|
|
113
|
+
const originalOn = process.on;
|
|
114
|
+
const originalExit = process.exit;
|
|
115
|
+
// Hide the Node-specific surface so installShutdownHandlers takes the no-op branch.
|
|
116
|
+
process.on = undefined;
|
|
117
|
+
process.exit = undefined;
|
|
118
|
+
try {
|
|
119
|
+
const exitRec = makeExitRecorder();
|
|
120
|
+
let closeCalled = false;
|
|
121
|
+
const uninstall = installShutdownHandlers(async () => {
|
|
122
|
+
closeCalled = true;
|
|
123
|
+
}, { exit: exitRec.exit, log: silentLog });
|
|
124
|
+
// The uninstall must be callable without throwing even in the no-op path.
|
|
125
|
+
uninstall();
|
|
126
|
+
assert.strictEqual(typeof uninstall, "function");
|
|
127
|
+
assert.strictEqual(closeCalled, false);
|
|
128
|
+
assert.strictEqual(exitRec.exitCode, null);
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
process.on = originalOn;
|
|
132
|
+
process.exit = originalExit;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
it("exits with code 1 when close rejects", async () => {
|
|
136
|
+
const exitRec = makeExitRecorder();
|
|
137
|
+
let errorLogged = null;
|
|
138
|
+
uninstallers.push(installShutdownHandlers(async () => {
|
|
139
|
+
throw new Error("boom");
|
|
140
|
+
}, {
|
|
141
|
+
exit: exitRec.exit,
|
|
142
|
+
log: {
|
|
143
|
+
info: () => { },
|
|
144
|
+
error: (msg) => {
|
|
145
|
+
errorLogged = msg;
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}));
|
|
149
|
+
process.emit("SIGTERM", "SIGTERM");
|
|
150
|
+
await exitRec.exited;
|
|
151
|
+
assert.strictEqual(exitRec.exitCode, 1);
|
|
152
|
+
assert.ok(errorLogged !== null, "expected error to be logged");
|
|
153
|
+
assert.match(errorLogged, /boom/);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../packages/extensions/ipc/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAIrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,4CAA4C;AAC5C,qBAAa,SAAS;IAKA,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ3C,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM;IAIzB,OAAO;IAEP,+CAA+C;IAC/C,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI3B,wBAAwB;IACxB,KAAK,IAAI,IAAI;CAGd;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../packages/extensions/ipc/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAIrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,4CAA4C;AAC5C,qBAAa,SAAS;IAKA,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ3C,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM;IAIzB,OAAO;IAEP,+CAA+C;IAC/C,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAI3B,wBAAwB;IACxB,KAAK,IAAI,IAAI;CAGd;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,UAAU,cAuFhG"}
|
|
@@ -87,6 +87,13 @@ export function startIpcServer(name, newMessageHandler) {
|
|
|
87
87
|
controller.abort();
|
|
88
88
|
// unrefing
|
|
89
89
|
server.unref();
|
|
90
|
+
// Windows named pipes are cleaned up by the OS; Unix sockets are not.
|
|
91
|
+
if (!isWindows) {
|
|
92
|
+
try {
|
|
93
|
+
fs.unlinkSync(socketPath);
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
}
|
|
90
97
|
};
|
|
91
98
|
}
|
|
92
99
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FuzzVersion } from "#@typeberry/ext-ipc";
|
|
2
2
|
import { v1 as fuzzV1 } from "#@typeberry/fuzz-proto";
|
|
3
|
+
import { type Closer } from "#@typeberry/utils";
|
|
3
4
|
import type { JamConfig } from "./jam-config.js";
|
|
4
5
|
export type FuzzConfig = {
|
|
5
6
|
version: FuzzVersion;
|
|
@@ -24,5 +25,7 @@ export declare function getFuzzDetails(): {
|
|
|
24
25
|
nodeVersion: fuzzV1.Version;
|
|
25
26
|
gpVersion: fuzzV1.Version;
|
|
26
27
|
};
|
|
27
|
-
export declare function mainFuzz(fuzzConfig: FuzzConfig, withRelPath: (v: string) => string): Promise<
|
|
28
|
+
export declare function mainFuzz(fuzzConfig: FuzzConfig, withRelPath: (v: string) => string): Promise<{
|
|
29
|
+
close: Closer;
|
|
30
|
+
}>;
|
|
28
31
|
//# sourceMappingURL=main-fuzz.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAAE,KAAK,MAAM,EAAoC,MAAM,kBAAkB,CAAC;AAGjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAoBF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASpF;AAED,iFAAiF;AACjF,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAsKrH"}
|
|
@@ -75,6 +75,9 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
75
75
|
// every reset, because opening it is the slow part. Only the in-memory blocks
|
|
76
76
|
// and leaf sets are rebuilt for each vector. fjall-hybrid only.
|
|
77
77
|
let fjallSession = null;
|
|
78
|
+
// Set when close() starts. Guards resetState so a fuzz command arriving
|
|
79
|
+
// mid-shutdown can't build a fresh node that close() then orphans.
|
|
80
|
+
let isClosing = false;
|
|
78
81
|
const chainSpec = getChainSpec(config.node.flavor);
|
|
79
82
|
const closeFuzzTarget = startFuzzTarget(fuzzConfig.version, fuzzConfig.socket, {
|
|
80
83
|
...getFuzzDetails(),
|
|
@@ -99,6 +102,9 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
99
102
|
return runningNode.getStateEntries(hash);
|
|
100
103
|
},
|
|
101
104
|
resetState: async (header, state, ancestry) => {
|
|
105
|
+
if (isClosing) {
|
|
106
|
+
return Bytes.zero(HASH_SIZE).asOpaque();
|
|
107
|
+
}
|
|
102
108
|
if (runningNode !== null) {
|
|
103
109
|
const finish = runningNode.close();
|
|
104
110
|
runningNode = null;
|
|
@@ -175,20 +181,29 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
175
181
|
return await runningNode.getBestStateRootHash();
|
|
176
182
|
},
|
|
177
183
|
});
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
184
|
+
return {
|
|
185
|
+
close: async () => {
|
|
186
|
+
isClosing = true;
|
|
187
|
+
// Stop accepting connections + unlink the socket.
|
|
188
|
+
closeFuzzTarget();
|
|
189
|
+
// Drain the active session (flush + close DB). Swallow errors so a
|
|
190
|
+
// failing close still lets the process exit 0; the db is wiped next.
|
|
191
|
+
// The node references the shared fjall session, so it must close first.
|
|
192
|
+
if (runningNode !== null) {
|
|
193
|
+
const node = runningNode;
|
|
194
|
+
runningNode = null;
|
|
195
|
+
await node.close().catch((e) => logger.error `Error closing fuzz node: ${e}`);
|
|
196
|
+
}
|
|
197
|
+
// Release the reused fjall values keyspace before wiping its files.
|
|
198
|
+
if (fjallSession !== null) {
|
|
199
|
+
const session = fjallSession;
|
|
200
|
+
fjallSession = null;
|
|
201
|
+
await session.close().catch((e) => logger.error `Error closing fjall session: ${e}`);
|
|
202
|
+
}
|
|
203
|
+
if (fuzzDbBase !== undefined) {
|
|
204
|
+
await wipeFuzzDb(fuzzDbBase).catch(() => { });
|
|
205
|
+
}
|
|
206
|
+
},
|
|
192
207
|
};
|
|
193
208
|
}
|
|
194
209
|
function isValidStateBackend(val) {
|