smoldot 1.0.5 → 1.0.7

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.
@@ -68,6 +68,7 @@ function start(options, wasmModule, platformBindings) {
68
68
  currentTaskName: null,
69
69
  connections: new Map(),
70
70
  addChainResults: [],
71
+ onExecutorShutdownOrWasmPanic: () => { },
71
72
  chains: new Map(),
72
73
  };
73
74
  // Callback called during the execution of the instance.
@@ -97,6 +98,15 @@ function start(options, wasmModule, platformBindings) {
97
98
  }
98
99
  state.chains.clear();
99
100
  state.currentTaskName = null;
101
+ const cb = state.onExecutorShutdownOrWasmPanic;
102
+ state.onExecutorShutdownOrWasmPanic = () => { };
103
+ cb();
104
+ break;
105
+ }
106
+ case "executor-shutdown": {
107
+ const cb = state.onExecutorShutdownOrWasmPanic;
108
+ state.onExecutorShutdownOrWasmPanic = () => { };
109
+ cb();
100
110
  break;
101
111
  }
102
112
  case "log": {
@@ -342,7 +352,7 @@ function start(options, wasmModule, platformBindings) {
342
352
  throw state.instance.error;
343
353
  if (state.instance.status !== "ready")
344
354
  throw new Error(); // Internal error. Never supposed to happen.
345
- state.instance.instance.startShutdown();
355
+ state.instance.instance.shutdownExecutor();
346
356
  state.instance = { status: "destroyed", error: new public_types_js_1.AlreadyDestroyedError() };
347
357
  state.connections.forEach((connec) => connec.reset());
348
358
  state.connections.clear();
@@ -358,6 +368,8 @@ function start(options, wasmModule, platformBindings) {
358
368
  }
359
369
  state.chains.clear();
360
370
  state.currentTaskName = null;
371
+ // Wait for the `executor-shutdown` event to be generated.
372
+ yield new Promise((resolve) => state.onExecutorShutdownOrWasmPanic = resolve);
361
373
  })
362
374
  };
363
375
  }
@@ -51,6 +51,8 @@ export type Event = {
51
51
  } | {
52
52
  ty: "wasm-panic";
53
53
  message: string;
54
+ } | {
55
+ ty: "executor-shutdown";
54
56
  } | {
55
57
  ty: "new-connection";
56
58
  connectionId: number;
@@ -95,12 +97,14 @@ export interface Instance {
95
97
  addChain: (chainSpec: string, databaseContent: string, potentialRelayChains: number[], disableJsonRpc: boolean) => void;
96
98
  removeChain: (chainId: number) => void;
97
99
  /**
98
- * Starts the instance shutdown process in the background. After this function returns, no
99
- * more event is generated and all further function calls might throw or have no effect.
100
+ * Notifies the background executor that it should stop. Once it has effectively stopped,
101
+ * a `executor-shutdown` event will be generated.
102
+ * Note that the instance can technically still be used, and all the functions still work, but
103
+ * in practice nothing is being run in the background and as such it won't do much.
100
104
  * Existing connections are *not* closed. It is the responsibility of the API user to close
101
105
  * all connections.
102
106
  */
103
- startShutdown: () => void;
107
+ shutdownExecutor: () => void;
104
108
  connectionOpened: (connectionId: number, info: {
105
109
  type: 'single-stream';
106
110
  handshake: 'multistream-select-noise-yamux';
@@ -44,6 +44,7 @@ function startLocalInstance(config, wasmModule, eventCallback) {
44
44
  advanceExecutionPromise: null,
45
45
  stdoutBuffer: "",
46
46
  stderrBuffer: "",
47
+ onShutdownExecutorOrWasmPanic: () => { }
47
48
  };
48
49
  const smoldotJsBindings = {
49
50
  // Must exit with an error. A human-readable message can be found in the WebAssembly
@@ -55,6 +56,8 @@ function startLocalInstance(config, wasmModule, eventCallback) {
55
56
  len >>>= 0;
56
57
  const message = buffer.utf8BytesToString(new Uint8Array(instance.exports.memory.buffer), ptr, len);
57
58
  eventCallback({ ty: "wasm-panic", message });
59
+ state.onShutdownExecutorOrWasmPanic();
60
+ state.onShutdownExecutorOrWasmPanic = () => { };
58
61
  throw new Error();
59
62
  },
60
63
  buffer_size: (bufferIndex) => {
@@ -287,6 +290,8 @@ function startLocalInstance(config, wasmModule, eventCallback) {
287
290
  proc_exit: (retCode) => {
288
291
  state.instance = null;
289
292
  eventCallback({ ty: "wasm-panic", message: `proc_exit called: ${retCode}` });
293
+ state.onShutdownExecutorOrWasmPanic();
294
+ state.onShutdownExecutorOrWasmPanic = () => { };
290
295
  throw new Error();
291
296
  },
292
297
  // Return the number of environment variables and the total size of all environment
@@ -339,6 +344,9 @@ function startLocalInstance(config, wasmModule, eventCallback) {
339
344
  // Smoldot requires an initial call to the `init` function in order to do its internal
340
345
  // configuration.
341
346
  state.instance.exports.init(config.maxLogLevel);
347
+ // Promise that is notified when the `shutdownExecutor` function is called or when a Wasm
348
+ // panic happens.
349
+ const shutdownExecutorOrWasmPanicPromise = new Promise((resolve) => state.onShutdownExecutorOrWasmPanic = () => resolve("stop"));
342
350
  (() => __awaiter(this, void 0, void 0, function* () {
343
351
  const cpuRateLimit = config.cpuRateLimit;
344
352
  // In order to avoid calling `setTimeout` too often, we accumulate sleep up until
@@ -346,13 +354,10 @@ function startLocalInstance(config, wasmModule, eventCallback) {
346
354
  let missingSleep = 0;
347
355
  let now = config.performanceNow();
348
356
  while (true) {
349
- const whenReadyAgain = new Promise((resolve) => state.advanceExecutionPromise = resolve);
357
+ const whenReadyAgain = new Promise((resolve) => state.advanceExecutionPromise = () => resolve("ready"));
350
358
  if (!state.instance)
351
359
  break;
352
- const outcome = state.instance.exports.advance_execution();
353
- if (outcome === 0) {
354
- break;
355
- }
360
+ state.instance.exports.advance_execution();
356
361
  const afterExec = config.performanceNow();
357
362
  const elapsed = afterExec - now;
358
363
  now = afterExec;
@@ -369,9 +374,12 @@ function startLocalInstance(config, wasmModule, eventCallback) {
369
374
  // really care for such extreme values.
370
375
  if (missingSleep > 2147483646) // Doc says `> 2147483647`, but I don't really trust their pedanticism so let's be safe
371
376
  missingSleep = 2147483646;
372
- yield new Promise((resolve) => setTimeout(resolve, missingSleep));
377
+ const sleepFinished = new Promise((resolve) => setTimeout(() => resolve("timeout"), missingSleep));
378
+ if ((yield Promise.race([sleepFinished, shutdownExecutorOrWasmPanicPromise])) === "stop")
379
+ break;
373
380
  }
374
- yield whenReadyAgain;
381
+ if ((yield Promise.race([whenReadyAgain, shutdownExecutorOrWasmPanicPromise])) === "stop")
382
+ break;
375
383
  const afterWait = config.performanceNow();
376
384
  // `afterWait - now` is equal to how long we've waited for the `setTimeout` callback to
377
385
  // trigger. While in principle `afterWait - now` should be roughly equal to
@@ -386,6 +394,9 @@ function startLocalInstance(config, wasmModule, eventCallback) {
386
394
  missingSleep = -10000;
387
395
  now = afterWait;
388
396
  }
397
+ if (!state.instance)
398
+ return;
399
+ eventCallback({ ty: "executor-shutdown" });
389
400
  }))();
390
401
  return {
391
402
  request: (request, chainId) => {
@@ -447,12 +458,12 @@ function startLocalInstance(config, wasmModule, eventCallback) {
447
458
  return;
448
459
  state.instance.exports.remove_chain(chainId);
449
460
  },
450
- startShutdown: () => {
461
+ shutdownExecutor: () => {
451
462
  if (!state.instance)
452
463
  return;
453
- const instance = state.instance;
454
- state.instance = null;
455
- instance.exports.start_shutdown();
464
+ const cb = state.onShutdownExecutorOrWasmPanic;
465
+ state.onShutdownExecutorOrWasmPanic = () => { };
466
+ cb();
456
467
  },
457
468
  connectionOpened: (connectionId, info) => {
458
469
  if (!state.instance)
@@ -75,9 +75,8 @@ function connectToInstanceServer(config) {
75
75
  const message = messageEvent.data;
76
76
  // Update some local state.
77
77
  switch (message.ty) {
78
- case "wasm-panic": {
79
- state.jsonRpcResponses.clear();
80
- state.connections.clear();
78
+ case "wasm-panic":
79
+ case "executor-shutdown": {
81
80
  portToServer.close();
82
81
  initialPort.close();
83
82
  break;
@@ -171,7 +170,7 @@ function connectToInstanceServer(config) {
171
170
  portToServer.postMessage(msg);
172
171
  return item;
173
172
  },
174
- startShutdown() {
173
+ shutdownExecutor() {
175
174
  const msg = { ty: "shutdown" };
176
175
  portToServer.postMessage(msg);
177
176
  },
@@ -231,12 +230,13 @@ function startInstanceServer(config, initPortToClient) {
231
230
  }
232
231
  break;
233
232
  }
233
+ case "executor-shutdown":
234
234
  case "wasm-panic": {
235
- state.instance = null;
236
- state.connections.clear();
237
- state.acceptedJsonRpcResponses.clear();
238
- if (state.onShutdown)
239
- state.onShutdown();
235
+ if (state.onExecutorShutdownOrWasmPanic) {
236
+ const cb = state.onExecutorShutdownOrWasmPanic;
237
+ delete state.onExecutorShutdownOrWasmPanic;
238
+ cb();
239
+ }
240
240
  break;
241
241
  }
242
242
  case "json-rpc-responses-non-empty": {
@@ -244,8 +244,6 @@ function startInstanceServer(config, initPortToClient) {
244
244
  // from within the events callback itself.
245
245
  // TODO: do better than setTimeout?
246
246
  setTimeout(() => {
247
- if (!state.instance)
248
- return;
249
247
  const numAccepted = state.acceptedJsonRpcResponses.get(event.chainId);
250
248
  if (numAccepted == 0)
251
249
  return;
@@ -274,6 +272,9 @@ function startInstanceServer(config, initPortToClient) {
274
272
  const ev = event;
275
273
  portToClient.postMessage(ev);
276
274
  };
275
+ // We create the `Promise` ahead of time in order to potentially catch potential `wasm-panic`
276
+ // events as early as during initialization.
277
+ const execFinishedPromise = new Promise((resolve) => state.onExecutorShutdownOrWasmPanic = resolve);
277
278
  state.instance = yield instance.startLocalInstance(Object.assign({ forbidTcp,
278
279
  forbidWs,
279
280
  forbidNonLocalWs,
@@ -283,8 +284,6 @@ function startInstanceServer(config, initPortToClient) {
283
284
  maxLogLevel }, config), wasmModule, eventsCallback);
284
285
  portToClient.onmessage = (messageEvent) => {
285
286
  const message = messageEvent.data;
286
- if (!state.instance)
287
- return;
288
287
  switch (message.ty) {
289
288
  case "add-chain": {
290
289
  state.instance.addChain(message.chainSpec, message.databaseContent, message.potentialRelayChains, message.disableJsonRpc);
@@ -311,10 +310,7 @@ function startInstanceServer(config, initPortToClient) {
311
310
  break;
312
311
  }
313
312
  case "shutdown": {
314
- if (state.onShutdown)
315
- state.onShutdown();
316
- state.instance.startShutdown();
317
- portToClient.close();
313
+ state.instance.shutdownExecutor();
318
314
  break;
319
315
  }
320
316
  case "connection-reset": {
@@ -372,10 +368,7 @@ function startInstanceServer(config, initPortToClient) {
372
368
  }
373
369
  }
374
370
  };
375
- // The instance might already have crashed. Handle this situation.
376
- if (!state.instance)
377
- return Promise.resolve();
378
- return new Promise((resolve) => state.onShutdown = resolve);
371
+ return execFinishedPromise;
379
372
  });
380
373
  }
381
374
  exports.startInstanceServer = startInstanceServer;