@camstack/addon-pipeline 1.1.29 → 1.1.31

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.
@@ -336,6 +336,51 @@ var PoolHandle = class {
336
336
  }
337
337
  async dispose() {}
338
338
  };
339
+ /**
340
+ * How long to wait for a worker to exit on SIGTERM before escalating to
341
+ * SIGKILL. A worker idle between frames exits well under this; only a worker
342
+ * stuck in a native inference call (openvino/onnx C++) needs the escalation.
343
+ */
344
+ var POOL_WORKER_TERM_GRACE_MS = 2e3;
345
+ /**
346
+ * Terminate a spawned child GRACEFULLY THEN FORCEFULLY: end stdin, send
347
+ * SIGTERM, and if the process hasn't exited within `graceMs`, send SIGKILL.
348
+ * Resolves once the process has exited (or was already dead).
349
+ *
350
+ * The old teardown sent SIGTERM and dropped the reference immediately — a
351
+ * worker busy in a native inference loop ignores SIGTERM and ORPHANS at full
352
+ * CPU (observed after an engine re-spin). SIGKILL cannot be ignored, so the
353
+ * escalation guarantees the process dies. Exported for tests.
354
+ */
355
+ async function terminateChild(proc, graceMs) {
356
+ if (proc.exitCode !== null || proc.signalCode !== null) return;
357
+ try {
358
+ proc.stdin?.end();
359
+ } catch {}
360
+ await new Promise((resolve) => {
361
+ let settled = false;
362
+ let timer = null;
363
+ const done = () => {
364
+ if (settled) return;
365
+ settled = true;
366
+ if (timer) clearTimeout(timer);
367
+ resolve();
368
+ };
369
+ proc.once("exit", done);
370
+ try {
371
+ proc.kill("SIGTERM");
372
+ } catch {
373
+ done();
374
+ return;
375
+ }
376
+ timer = setTimeout(() => {
377
+ try {
378
+ proc.kill("SIGKILL");
379
+ } catch {}
380
+ done();
381
+ }, graceMs);
382
+ });
383
+ }
339
384
  var PoolWorker = class {
340
385
  process = null;
341
386
  receiveBuffer = Buffer.alloc(0);
@@ -491,12 +536,11 @@ var PoolWorker = class {
491
536
  return await this.dispatch(MSG_COMMAND, payload);
492
537
  }
493
538
  async dispose() {
494
- if (this.process) {
495
- this.process.stdin?.end();
496
- this.process.kill("SIGTERM");
497
- this.process = null;
498
- this.ready = false;
499
- }
539
+ const proc = this.process;
540
+ if (!proc) return;
541
+ this.process = null;
542
+ this.ready = false;
543
+ await terminateChild(proc, POOL_WORKER_TERM_GRACE_MS);
500
544
  }
501
545
  dispatch(msgType, payload) {
502
546
  const reqId = this.allocRequestId();
@@ -6220,6 +6264,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
6220
6264
  const storedRuntime = store["engineRuntime"];
6221
6265
  const node = this.localProbeNodeId();
6222
6266
  const storedBackend = readNodeEngineValue(store, "engineBackend", node);
6267
+ if (storedBackend === "auto") return null;
6223
6268
  if (typeof storedBackend === "string" && storedBackend.length > 0 && storedBackend !== "auto") {
6224
6269
  const backend = storedBackend;
6225
6270
  const storedDeviceRaw = readNodeEngineValue(store, "engineDevice", node);
@@ -329,6 +329,51 @@ var PoolHandle = class {
329
329
  }
330
330
  async dispose() {}
331
331
  };
332
+ /**
333
+ * How long to wait for a worker to exit on SIGTERM before escalating to
334
+ * SIGKILL. A worker idle between frames exits well under this; only a worker
335
+ * stuck in a native inference call (openvino/onnx C++) needs the escalation.
336
+ */
337
+ var POOL_WORKER_TERM_GRACE_MS = 2e3;
338
+ /**
339
+ * Terminate a spawned child GRACEFULLY THEN FORCEFULLY: end stdin, send
340
+ * SIGTERM, and if the process hasn't exited within `graceMs`, send SIGKILL.
341
+ * Resolves once the process has exited (or was already dead).
342
+ *
343
+ * The old teardown sent SIGTERM and dropped the reference immediately — a
344
+ * worker busy in a native inference loop ignores SIGTERM and ORPHANS at full
345
+ * CPU (observed after an engine re-spin). SIGKILL cannot be ignored, so the
346
+ * escalation guarantees the process dies. Exported for tests.
347
+ */
348
+ async function terminateChild(proc, graceMs) {
349
+ if (proc.exitCode !== null || proc.signalCode !== null) return;
350
+ try {
351
+ proc.stdin?.end();
352
+ } catch {}
353
+ await new Promise((resolve) => {
354
+ let settled = false;
355
+ let timer = null;
356
+ const done = () => {
357
+ if (settled) return;
358
+ settled = true;
359
+ if (timer) clearTimeout(timer);
360
+ resolve();
361
+ };
362
+ proc.once("exit", done);
363
+ try {
364
+ proc.kill("SIGTERM");
365
+ } catch {
366
+ done();
367
+ return;
368
+ }
369
+ timer = setTimeout(() => {
370
+ try {
371
+ proc.kill("SIGKILL");
372
+ } catch {}
373
+ done();
374
+ }, graceMs);
375
+ });
376
+ }
332
377
  var PoolWorker = class {
333
378
  process = null;
334
379
  receiveBuffer = Buffer.alloc(0);
@@ -484,12 +529,11 @@ var PoolWorker = class {
484
529
  return await this.dispatch(MSG_COMMAND, payload);
485
530
  }
486
531
  async dispose() {
487
- if (this.process) {
488
- this.process.stdin?.end();
489
- this.process.kill("SIGTERM");
490
- this.process = null;
491
- this.ready = false;
492
- }
532
+ const proc = this.process;
533
+ if (!proc) return;
534
+ this.process = null;
535
+ this.ready = false;
536
+ await terminateChild(proc, POOL_WORKER_TERM_GRACE_MS);
493
537
  }
494
538
  dispatch(msgType, payload) {
495
539
  const reqId = this.allocRequestId();
@@ -6213,6 +6257,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
6213
6257
  const storedRuntime = store["engineRuntime"];
6214
6258
  const node = this.localProbeNodeId();
6215
6259
  const storedBackend = readNodeEngineValue(store, "engineBackend", node);
6260
+ if (storedBackend === "auto") return null;
6216
6261
  if (typeof storedBackend === "string" && storedBackend.length > 0 && storedBackend !== "auto") {
6217
6262
  const backend = storedBackend;
6218
6263
  const storedDeviceRaw = readNodeEngineValue(store, "engineDevice", node);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.1.29",
3
+ "version": "1.1.31",
4
4
  "description": "CamStack Pipeline bundle — runner, detection, motion, decoders, audio + stream broker. Multi-entry npm package shipping 7 addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",