@remix_labs/machine-starter 2.12820.0-dev → 2.12822.0-dev

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/common.js CHANGED
@@ -8,32 +8,10 @@ let last_hub = null;
8
8
 
9
9
  let workers = {};
10
10
  let worker_topics = {};
11
- // hub channel handed to each VM worker; the hub's end must be released when the worker dies
12
- let worker_chans = {};
13
11
  let children = {};
14
12
  let ffiLoops = {};
15
13
  let blobs = {};
16
14
  let blobsLoops = {};
17
- // vmID -> { cancelled } for a StartWASM2 that has not finished setting itself
18
- // up. terminateVM marks it cancelled so the start can unwind instead of
19
- // registering loops for a VM that was already stopped.
20
- let starting = {};
21
-
22
- // Diagnostics for slowdowns and "the ID is used" errors. Call
23
- // globalThis.__rmxMachineDiag() in the console: every map below should be empty
24
- // when no VM is running. A non-empty ffiLoops/blobsLoops/worker_topics entry
25
- // without a worker is a leak.
26
- const diag = { started: 0, terminated: 0, cancelledStarts: 0, terminateWhileStarting: 0, loopsClosed: 0, killPublishFailed: 0 };
27
- globalThis.__rmxMachineDiag = () => ({
28
- ...diag,
29
- workers: Object.keys(workers),
30
- worker_topics: Object.keys(worker_topics),
31
- ffiLoops: Object.keys(ffiLoops),
32
- blobsLoops: Object.keys(blobsLoops),
33
- children: Object.keys(children),
34
- blobs: Object.keys(blobs),
35
- starting: Object.keys(starting),
36
- });
37
15
 
38
16
  function terminateAll() {
39
17
  console.log("[machine-starter] terminate all VMs");
@@ -62,19 +40,6 @@ function terminateAll() {
62
40
  function terminateVM(vmID) {
63
41
  console.log("[machine-starter] terminate VM", vmID);
64
42
  let worker = workers[vmID];
65
- if (starting[vmID]) {
66
- // Stopped while StartWASM2 was still awaiting (fetching/compiling mixrt,
67
- // creating the starter mixcore). Tell it to unwind; it removes what it
68
- // registered. Without this it registered ffiLoops[vmID] after this
69
- // call and every later start of the same vmID failed with
70
- // "Cannot start VM, the ID is used".
71
- starting[vmID].cancelled = true;
72
- // free the id now: a start that follows this stop must not see the cancelled one
73
- // (the cancelled start still holds its token and unwinds itself)
74
- delete starting[vmID];
75
- diag.terminateWhileStarting++;
76
- console.warn("[machine-starter] terminate VM while it is still starting", vmID);
77
- }
78
43
  if (worker) {
79
44
  let vmChildren = children[vmID];
80
45
  let blobList = blobs[vmID] || [];
@@ -83,12 +48,8 @@ function terminateVM(vmID) {
83
48
  delete children[vmID];
84
49
  delete ffiLoops[vmID];
85
50
  delete blobsLoops[vmID];
86
- delete blobs[vmID];
87
51
  killVM(vmID);
88
- diag.terminated++;
89
- if (ffiLoop) {
90
- ffiLoop.terminate();
91
- }
52
+ ffiLoop.terminate();
92
53
  if (blobsLoop) {
93
54
  blobsLoop.terminate();
94
55
  }
@@ -111,39 +72,15 @@ function killVM(vmID) {
111
72
  delete worker_topics[vmID];
112
73
  if (topic && last_hub) {
113
74
  let payload = { _rmx_type:"msg_vm_register", id: vmID, alive: false };
114
- // The worker must be terminated even when the unannounce cannot be published:
115
- // a chain of .then() skipped worker.terminate() on any failure, leaking a Web Worker
116
- // (with its wasm instance) for every VM stopped that way.
117
75
  last_hub.newChannel().then(channel => {
118
76
  let msg = channel.newJSONMessage("msg_vm_register", "vm", payload);
119
- return channel.publish(topic + "/announce", msg, true).finally(() => closeChannel(channel));
120
- }).catch(e => {
121
- diag.killPublishFailed++;
122
- console.warn("[machine-starter] could not announce VM stop", vmID, e);
123
- }).finally(() => { worker.terminate(); releaseWorkerChannel(last_hub, vmID); });
77
+ channel.publish(topic + "/announce", msg, true)
78
+ }).then(_ => worker.terminate())
124
79
  } else {
125
- worker.terminate();
126
- releaseWorkerChannel(last_hub, vmID);
80
+ worker.terminate()
127
81
  }
128
82
  }
129
83
 
130
- // A hub channel has no explicit close: drop its MessagePort so the hub worker can release it.
131
- function closeChannel(channel) {
132
- try {
133
- if (typeof channel.close === "function") channel.close();
134
- else channel.port && channel.port.close && channel.port.close();
135
- // a message sent just before close() may not arrive: ask via the hub itself as well
136
- if (last_hub && last_hub.closeChannelById) last_hub.closeChannelById(channel.chanId);
137
- } catch (_) {}
138
- }
139
-
140
- // The channel given to a VM worker was transferred, so only the hub can release its end.
141
- function releaseWorkerChannel(hub, vmID) {
142
- let chanId = worker_chans[vmID];
143
- delete worker_chans[vmID];
144
- try { if (hub && hub.closeChannelById) hub.closeChannelById(chanId); } catch (_) {}
145
- }
146
-
147
84
  if (globalThis.Process) {
148
85
  Process.on("exit", () => {
149
86
  terminateAll()
@@ -196,60 +133,12 @@ async function StartWASM2(hub, config, getWorker) {
196
133
  console.log("[machine-starter] Call MixExplain() for explaining debug levels, and MixSetMask(m) for changing these")
197
134
  };
198
135
  last_hub = hub;
199
- if (starting[config.vmID]) {
200
- // another start of the same id has not finished setting itself up
201
- throw new Error("Cannot start VM, the ID is used: " + config.vmID);
202
- }
203
- // Registered before the first await: a terminateVM that arrives while the channel and worker
204
- // are still being created finds the token and cancels this start. Without it that stop was a
205
- // no-op, the VM registered and ran with no owner, and every later start failed with
206
- // "Cannot start VM, the ID is used".
207
- const startToken = { cancelled: false };
208
- starting[config.vmID] = startToken;
209
- let channel;
210
- let worker;
211
- try {
212
- channel = await hub.newChannel();
213
- // injected by either index.js or node.js
214
- worker = await getWorker();
215
- } catch (e) {
216
- if (starting[config.vmID] === startToken) delete starting[config.vmID];
217
- if (channel) closeChannel(channel);
218
- throw e;
219
- }
220
- if (startToken.cancelled) {
221
- diag.cancelledStarts++;
222
- console.warn("[machine-starter] start cancelled before the VM was registered", config.vmID);
223
- if (starting[config.vmID] === startToken) delete starting[config.vmID];
224
- closeChannel(channel);
225
- try { worker.terminate(); } catch (_) {}
226
- return worker;
227
- }
228
- if (workers[config.vmID] || ffiLoops[config.vmID]) {
229
- if (starting[config.vmID] === startToken) delete starting[config.vmID];
230
- closeChannel(channel);
136
+ let channel = await hub.newChannel();
137
+ // injected by either index.js or node.js
138
+ let worker = await getWorker();
139
+ if (workers[config.vmID] || ffiLoops[config.vmID])
231
140
  throw new Error("Cannot start VM, the ID is used: " + config.vmID);
232
- }
233
141
  workers[config.vmID] = worker;
234
- worker_chans[config.vmID] = channel.chanId;
235
- diag.started++;
236
- // Called after every await below: if terminateVM ran meanwhile, undo and stop.
237
- const abortIfCancelled = () => {
238
- if (!startToken.cancelled) return false;
239
- diag.cancelledStarts++;
240
- console.warn("[machine-starter] start cancelled, VM was terminated while starting", config.vmID);
241
- if (starting[config.vmID] === startToken) delete starting[config.vmID];
242
- // only undo what this start registered: a newer start of the same id may own the maps now
243
- if (workers[config.vmID] === worker) {
244
- delete ffiLoops[config.vmID];
245
- delete blobsLoops[config.vmID];
246
- delete worker_topics[config.vmID];
247
- delete workers[config.vmID];
248
- releaseWorkerChannel(hub, config.vmID);
249
- }
250
- try { worker.terminate(); } catch (_) {}
251
- return true;
252
- };
253
142
  if (config.topicPrefix != undefined && config.topicPrefix != "") {
254
143
  worker_topics[config.vmID] = config.topicPrefix + "/vm/" + config.vmID;
255
144
  } else {
@@ -259,11 +148,9 @@ async function StartWASM2(hub, config, getWorker) {
259
148
  // from now on, the "local" FFIs are called "starter" FFIs, because this
260
149
  // distinguishes these from other locally defined FFIs
261
150
  let mixcore = await createStarterMixcore(config.mixcore);
262
- if (abortIfCancelled()) return worker;
263
151
  if (mixcore) {
264
152
  // merge the ffis and store them back into the config so sub vms also get them
265
153
  config.localFFIs = Object.assign(starterFFIs, await mixcoreStarterFFIs(mixcore));
266
- if (abortIfCancelled()) return worker;
267
154
  // delete the mixcore config to avoid being handled further by the worker
268
155
  delete config.mixcore;
269
156
  }
@@ -297,9 +184,7 @@ async function StartWASM2(hub, config, getWorker) {
297
184
  globalThis.mixrtCodeURL = url;
298
185
  globalThis.mixrtCodeModule = mixrtCode;
299
186
  }
300
- if (abortIfCancelled()) return worker;
301
187
  };
302
- if (abortIfCancelled()) return worker;
303
188
  ffiLoops[config.vmID] = setupStarterFFIs(hub, config, starterFFIfuns);
304
189
  blobsLoops[config.vmID] = setupBlobs(hub, config);
305
190
  let config_msg =
@@ -325,7 +210,6 @@ async function StartWASM2(hub, config, getWorker) {
325
210
  // (http, https, data)
326
211
  };
327
212
  worker.postMessage(config_msg, [ channel.port ]);
328
- if (starting[config.vmID] === startToken) delete starting[config.vmID];
329
213
  return worker;
330
214
  }
331
215
 
@@ -350,7 +234,6 @@ async function ffi_vm_start(conn, args, getWorker) {
350
234
  let vmChildren = children[conn.config.vmID];
351
235
  if (!vmChildren) vmChildren = [];
352
236
  vmChildren.push(vm_id);
353
- children[conn.config.vmID] = vmChildren; // was never stored: embedded VMs were not stopped with their parent
354
237
  let arg_config = args[0];
355
238
  if (arg_config.interceptFFI !== undefined)
356
239
  config["interceptFFI"] = arg_config.interceptFFI;
@@ -364,10 +247,6 @@ async function ffi_vm_stop(conn, args) {
364
247
  let vm_id = args[0];
365
248
  let worker = workers[vm_id];
366
249
  if (!worker) throw new Error("no such VM: " + vm_id);
367
- for (let parent of Object.keys(children)) {
368
- let i = children[parent].indexOf(vm_id);
369
- if (i >= 0) children[parent].splice(i, 1);
370
- }
371
250
  terminateVM(vm_id);
372
251
  return null;
373
252
  }
@@ -510,11 +389,6 @@ async function starterFFIsMessageLoop(hub, config, starterFFIs, terminate) {
510
389
  ]);
511
390
  if (terminated) {
512
391
  // console.log("[machine-starter] terminate FFI loop", vmID);
513
- // Release the hub subscription and channel; without this each VM (one per build in the
514
- // builder) left both behind for the lifetime of the page.
515
- await sub.unsubscribe().catch(() => {});
516
- closeChannel(channel);
517
- diag.loopsClosed++;
518
392
  return;
519
393
  }
520
394
  let name = msg.payload.name;
@@ -575,9 +449,6 @@ async function blobsMessageLoop(hub, config, terminate) {
575
449
  ]);
576
450
  if (terminated) {
577
451
  // console.log("[machine-starter] terminate blobs loop", vmID);
578
- await sub.unsubscribe().catch(() => {});
579
- closeChannel(channel);
580
- diag.loopsClosed++;
581
452
  return;
582
453
  }
583
454
  let name = msg.payload.name;
@@ -645,20 +516,7 @@ class FFIComm {
645
516
  }
646
517
  }
647
518
 
648
- // Stops every VM that `parentVmID` started (through $vm_start) and has not stopped itself, including
649
- // ones that are still starting. Returns how many were stopped. A parent that outlives its children
650
- // (the L2 session agent starts one embedded VM per session) can leave one running when its owner
651
- // goes away before the session finished starting; the caller decides when that has happened.
652
- function terminateChildren(parentVmID) {
653
- let list = children[parentVmID];
654
- if (!list || list.length === 0) return 0;
655
- let ids = list.slice();
656
- children[parentVmID] = [];
657
- for (let id of ids) terminateVM(id);
658
- return ids.length;
659
- }
660
-
661
519
  // use terminateVM to shut a worker down!
662
- export { StartWASM, StartWASM2, terminateAll, terminateVM, terminateChildren,
520
+ export { StartWASM, StartWASM2, terminateAll, terminateVM,
663
521
  Token, Case, Opaque
664
522
  }
@@ -1,2 +1,2 @@
1
- var GROOVEBOX_BUILD = "12820";
1
+ var GROOVEBOX_BUILD = "12822";
2
2
  export { GROOVEBOX_BUILD }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix_labs/machine-starter",
3
- "version": "2.12820.0-dev",
3
+ "version": "2.12822.0-dev",
4
4
  "description": "start the groove",
5
5
  "main": "node.js",
6
6
  "browser": "index.js",
@@ -11,7 +11,7 @@
11
11
  "author": "Remixlabs staff",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
- "@remix_labs/hub-client": "2.12820.0-dev",
14
+ "@remix_labs/hub-client": "2.12822.0-dev",
15
15
  "nanoid": "^5.0.2",
16
16
  "web-worker": "^1.2.0"
17
17
  },