@remix_labs/machine-starter 1.1460.0-dev → 1.1466.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.
Files changed (3) hide show
  1. package/index.js +94 -32
  2. package/package.json +2 -2
  3. package/start.js +2 -1
package/index.js CHANGED
@@ -1,14 +1,34 @@
1
1
  import { nanoid } from "nanoid";
2
2
  import { GROOVEBOX_BUILD } from "./groovebox_build.js";
3
3
 
4
- let terminate = function(f) {};
5
- if (globalThis.ThisIsNode) {
6
- terminate = function(f) {
7
- Process.on("exit", () => {
8
- console.log("terminating machine worker");
9
- f()
10
- })
4
+ let workers = {};
5
+ let parents = {};
6
+
7
+ function terminateAll() {
8
+ for (let [vmID, worker] of Object.entries(workers)) {
9
+ worker.terminate();
10
+ };
11
+ workers = {};
12
+ parents = {};
13
+ }
14
+
15
+ function terminateVM(vmID) {
16
+ let worker = workers[vmID];
17
+ if (worker) {
18
+ delete workers[vmID];
19
+ delete parents[vmID];
20
+ worker.terminate();
21
+ for (let [subID, parentID] of Object.entries(parents)) {
22
+ terminateVM(subID);
23
+ };
11
24
  }
25
+ }
26
+
27
+ if (globalThis.ThisIsNode) {
28
+ Process.on("exit", () => {
29
+ console.log("terminating machine workers");
30
+ terminateAll()
31
+ })
12
32
  };
13
33
 
14
34
  function CreateVMID() {
@@ -16,8 +36,14 @@ function CreateVMID() {
16
36
  }
17
37
 
18
38
  let initialMask = 64 + 1024; // default for web
39
+
19
40
  globalThis.MixSetMask = (m => {
20
41
  initialMask = m;
42
+ for (let [vmID, worker] of Object.entries(workers)) {
43
+ worker.postMessage({ "_rmx_type": "msg_vm_logMask",
44
+ "mask": m
45
+ })
46
+ }
21
47
  });
22
48
 
23
49
  function StartWASM(hub, baseURL, org, workspace, vmID, user, token, noOutputViaMQTT) {
@@ -51,20 +77,29 @@ function StartWASM2(hub, config) {
51
77
  fetch(code_url, {cache:"default"}).then(resp => { return resp.arrayBuffer() });
52
78
  bundle = new URL("/g/machine-wasm-core.js", window.location.href);
53
79
  }
54
- let localFFIs = {};
80
+ let localFFIkind = {};
81
+ let localFFIfuns = {};
55
82
  if (config.localFFIs) {
56
83
  for (const [k, v] of Object.entries(config.localFFIs)) {
84
+ localFFIfuns[k] = {...v};
57
85
  if (v.isRaw && (v.canFail || v.useJsonDecoder)) {
58
86
  console.error("A raw, local FFI must neither be failing nor use the JSON decoder: " + k);
59
87
  } else {
60
- localFFIs[k] = {...v};
61
- delete localFFIs[k].run;
88
+ localFFIkind[k] = {...v};
89
+ delete localFFIkind[k].run;
62
90
  }
63
- }
91
+ };
92
+ let vm_kind = { isRaw:false, canFail:false, useJsonDecoder:false};
93
+ localFFIkind["$vm_start"] = vm_kind;
94
+ localFFIfuns["$vm_start"] = ffi_vm_start;
95
+ localFFIkind["$vm_stop"] = vm_kind;
96
+ localFFIfuns["$vm_stop"] = ffi_vm_stop;
64
97
  }
65
98
  let worker = new Worker(bundle);
99
+ workers[config.vmID] = worker;
66
100
  let config_msg =
67
101
  { "_rmx_type": "msg_vm_configure",
102
+ "workerURL": bundle.toString(),
68
103
  "baseURL": config.baseURL,
69
104
  "org": config.org,
70
105
  "workspace": config.workspace,
@@ -75,23 +110,47 @@ function StartWASM2(hub, config) {
75
110
  "outputViaMQTT": !(config.noOutputViaMQTT),
76
111
  "machType": config.machType,
77
112
  "debugMask": initialMask,
78
- "localFFIs": localFFIs,
113
+ "localFFIs": localFFIkind,
79
114
  "grooveboxUrlPrefix": globalThis.GROOVEBOX_URL_PREFIX,
80
115
  "allowInsecureHttp": globalThis.GROOVEBOX_ALLOW_INSECURE_HTTP,
116
+ "jsonFFI": config.jsonFFI,
117
+ "interceptFFI": config.interceptFFI,
118
+ "interceptDynloadFFI": config.interceptDynloadFFI,
81
119
  };
82
120
  worker.postMessage(config_msg, [ channel.port ]);
83
- terminate(() => worker.terminate());
84
- globalThis.MixSetMask = (m => {
85
- worker.postMessage({ "_rmx_type": "msg_vm_logMask",
86
- "mask": m
87
- })
88
- });
89
121
  if (config.localFFIs)
90
- setupLocalFFIs(hub, config); // don't await!
122
+ setupLocalFFIs(hub, config, localFFIfuns); // don't await!
91
123
  return worker;
92
124
  })
93
125
  }
94
126
 
127
+ async function ffi_vm_start(conn, args) {
128
+ let vm_id = "embedded." + nanoid();
129
+ let config = {};
130
+ for (const [k, v] of Object.entries(conn.config)) {
131
+ config[k] = v;
132
+ }
133
+ config["vmID"] = vm_id;
134
+ parents[vm_id] = conn.config.vmID;
135
+ let arg_config = args[0];
136
+ if (arg_config.jsonFFI !== undefined)
137
+ config["jsonFFI"] = arg_config.jsonFFI;
138
+ if (arg_config.interceptFFI !== undefined)
139
+ config["interceptFFI"] = arg_config.interceptFFI;
140
+ if (arg_config.interceptDynloadFFI !== undefined)
141
+ config["interceptDynloadFFI"] = arg_config.interceptDynloadFFI;
142
+ let worker = await StartWASM2(conn.hub, config);
143
+ return vm_id;
144
+ }
145
+
146
+ async function ffi_vm_stop(conn, args) {
147
+ let vm_id = args[0];
148
+ let worker = workers[vm_id];
149
+ if (!worker) throw new Error("no such VM: " + vm_id);
150
+ terminateVM(vm_id);
151
+ return null;
152
+ }
153
+
95
154
  class Token {
96
155
  constructor(data, enabled, extras) {
97
156
  // data: must be an Uint8Array
@@ -207,13 +266,13 @@ function decode(val) {
207
266
  }
208
267
  }
209
268
 
210
- async function setupLocalFFIs(hub, config) {
269
+ async function setupLocalFFIs(hub, config, localFFIs) {
270
+ let vmID = config.vmID;
211
271
  let channel = await hub.newChannel();
212
- let comm = new FFIComm(hub, channel);
213
- let localFFIs = config.localFFIs;
214
- await channel.setLocalSubTopic("/local/ffi/call");
215
- await channel.setLocalPubTopic("/local/ffi/return");
216
- let sub = await channel.subscribe("/local/ffi/call");
272
+ let comm = new FFIComm(vmID, hub, channel);
273
+ await channel.setLocalSubTopic("/local/" + vmID);
274
+ await channel.setLocalPubTopic("/local/" + vmID);
275
+ let sub = await channel.subscribe("/local/" + vmID + "/ffi/call");
217
276
  while (true) {
218
277
  let msg = await sub.next();
219
278
  let name = msg.payload.name;
@@ -227,14 +286,15 @@ async function setupLocalFFIs(hub, config) {
227
286
  { call_id: call_id,
228
287
  hub: hub,
229
288
  channel: channel,
289
+ config: config,
230
290
  state: config.state,
231
291
  };
232
292
  let r = fun instanceof Function ? fun(connector, args) : fun.run(connector, args);
233
293
  if (r instanceof Promise) {
294
+ r.catch(_ => null); // prevent "unhandled rejection"
234
295
  await comm.later(call_id);
235
- r.then(
236
- (value) => comm.returnOrFail(fun, call_id, value),
237
- (reason) => comm.error(call_id, reason.message, reason.stack));
296
+ let value = await r;
297
+ await comm.returnOrFail(fun, call_id, value);
238
298
  } else {
239
299
  await comm.returnOrFail(fun, call_id, r);
240
300
  }
@@ -245,7 +305,8 @@ async function setupLocalFFIs(hub, config) {
245
305
  }
246
306
 
247
307
  class FFIComm {
248
- constructor(hub, channel) {
308
+ constructor(vmID, hub, channel) {
309
+ this.vmID = vmID;
249
310
  this.hub = hub;
250
311
  this.channel = channel;
251
312
  }
@@ -254,7 +315,7 @@ class FFIComm {
254
315
  { call_id: call_id,
255
316
  };
256
317
  let response = this.hub.newLocalMessage("msg_ffi_later", "starter", r_payload);
257
- return this.channel.publish("/local/ffi/return", response, false);
318
+ return this.channel.publish("/local/" + this.vmID + "/ffi/return", response, false);
258
319
  }
259
320
  return_(fun, call_id, result) {
260
321
  let r_payload =
@@ -262,7 +323,7 @@ class FFIComm {
262
323
  value: fun.useJsonDecoder ? result : encode(result)
263
324
  };
264
325
  let response = this.hub.newLocalMessage("msg_ffi_return", "starter", r_payload);
265
- return this.channel.publish("/local/ffi/return", response, false);
326
+ return this.channel.publish("/local/" + this.vmID + "/ffi/return", response, false);
266
327
  }
267
328
  error(call_id, message, stack) {
268
329
  let r_payload =
@@ -271,7 +332,7 @@ class FFIComm {
271
332
  stack: stack === undefined ? [] : stack,
272
333
  };
273
334
  let response = this.hub.newLocalMessage("msg_ffi_error", "starter", r_payload);
274
- this.channel.publish("/local/ffi/return", response, false);
335
+ this.channel.publish("/local/" + this.vmID + "/ffi/return", response, false);
275
336
  }
276
337
  returnOrFail(fun, call_id, result) {
277
338
  if (fun.canFail) {
@@ -302,5 +363,6 @@ class FFIComm {
302
363
  // use worker.terminate() to shut a worker down!
303
364
 
304
365
  export { CreateVMID, StartWASM, StartWASM2,
366
+ terminateAll, terminateVM,
305
367
  Token, Case, Opaque
306
368
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix_labs/machine-starter",
3
- "version": "1.1460.0-dev",
3
+ "version": "1.1466.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": "1.1460.0-dev",
14
+ "@remix_labs/hub-client": "1.1466.0-dev",
15
15
  "nanoid": "^3.1.12",
16
16
  "web-worker": "^1.2.0"
17
17
  },
package/start.js CHANGED
@@ -45,7 +45,8 @@ w.configure({wsURL: mqttURL, user:mqttUser, token:mqttToken}).then(resp => {
45
45
  user: mqttUser,
46
46
  token: mqttToken,
47
47
  noOutputViaMQTT: noOutputViaMQTT,
48
- machType: variant
48
+ machType: variant,
49
+ localFFIs: {},
49
50
  });
50
51
  break
51
52
  default: