Add ack message for worklet death; remove max pool size

This commit is contained in:
Aria 2026-01-12 13:51:21 -06:00
parent 753dc48b69
commit 7c5f53f9ae
4 changed files with 34 additions and 9 deletions

View file

@ -8,7 +8,6 @@ This program is free software: you can redistribute it and/or modify it under th
const nodePools = new Map(); const nodePools = new Map();
const POOL_KEY = Symbol('nodePoolKey'); const POOL_KEY = Symbol('nodePoolKey');
const IS_WORKLET_DEAD = Symbol('nodePoolIsWorkletDead'); const IS_WORKLET_DEAD = Symbol('nodePoolIsWorkletDead');
const MAX_POOL_SIZE = 64;
export const isPoolable = (node) => !!node[POOL_KEY]; export const isPoolable = (node) => !!node[POOL_KEY];
@ -47,10 +46,8 @@ export const releaseNodeToPool = (node) => {
const now = node.context?.currentTime ?? 0; const now = node.context?.currentTime ?? 0;
getParams(node).forEach((param) => param.cancelScheduledValues(now)); getParams(node).forEach((param) => param.cancelScheduledValues(now));
const pool = nodePools.get(key) ?? []; const pool = nodePools.get(key) ?? [];
if (pool.length < MAX_POOL_SIZE) {
pool.push(new WeakRef(node)); pool.push(new WeakRef(node));
nodePools.set(key, pool); nodePools.set(key, pool);
}
}; };
export const markWorkletAsDead = (worklet) => (worklet[IS_WORKLET_DEAD] = true); export const markWorkletAsDead = (worklet) => (worklet[IS_WORKLET_DEAD] = true);

View file

@ -186,7 +186,10 @@ export function registerSynthSounds() {
}); });
o.port.postMessage({ type: 'initialize' }); o.port.postMessage({ type: 'initialize' });
o.port.onmessage = (e) => { o.port.onmessage = (e) => {
if (e.data.type === 'died') markWorkletAsDead(o); if (e.data.type === 'died') {
markWorkletAsDead(o);
o.port.postMessage({ type: 'diedACK' });
}
o.port.onmessage = null; o.port.onmessage = null;
}; };
const gainAdjustment = 1 / Math.sqrt(voices); const gainAdjustment = 1 / Math.sqrt(voices);

View file

@ -252,7 +252,10 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
}); });
source.port.postMessage({ type: 'initialize', payload }); source.port.postMessage({ type: 'initialize', payload });
source.port.onmessage = (e) => { source.port.onmessage = (e) => {
if (e.data.type === 'died') markWorkletAsDead(source); if (e.data.type === 'died') {
markWorkletAsDead(source);
source.port.postMessage({ type: 'diedACK' });
}
source.port.onmessage = null; source.port.onmessage = null;
}; };
if (ac.currentTime > t) { if (ac.currentTime > t) {

View file

@ -464,11 +464,19 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
constructor() { constructor() {
super(); super();
this.isAlive = true; // used internally to prevent multiple death messages this.isAlive = true; // used internally to prevent multiple death messages
// diedACK is used for the main thread to acknowledge that the worklet has died
// This is so that we don't risk simultaneously killing the worklet (`return false`)
// and pooling the worklet (main thread) before the async `died` message hits
// the main thread
this.diedACK = false;
this.port.onmessage = (e) => { this.port.onmessage = (e) => {
const { type, payload } = e.data || {}; const { type, payload } = e.data || {};
if (type === 'initialize') { if (type === 'initialize') {
this.initialize(payload); this.initialize(payload);
} }
if (type === 'diedACK') {
this.diedACK = true;
}
}; };
this.initialize(); this.initialize();
} }
@ -529,8 +537,11 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
this.port.postMessage({ type: 'died' }); this.port.postMessage({ type: 'died' });
this.isAlive = false; this.isAlive = false;
} }
if (this.diedACK || currentTime >= params.end[1] + 1) {
return false; return false;
} }
return true;
}
if (currentTime >= params.end[0] || currentTime <= params.begin[0]) { if (currentTime >= params.end[0] || currentTime <= params.begin[0]) {
// Inside of grace period or not yet started // Inside of grace period or not yet started
return true; return true;
@ -1165,11 +1176,19 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
constructor(options) { constructor(options) {
super(options); super(options);
this.isAlive = true; // used internally to prevent multiple death messages this.isAlive = true; // used internally to prevent multiple death messages
// diedACK is used for the main thread to acknowledge that the worklet has died
// This is so that we don't risk simultaneously killing the worklet (`return false`)
// and pooling the worklet (main thread) before the async `died` message hits
// the main thread
this.diedACK = false;
this.port.onmessage = (e) => { this.port.onmessage = (e) => {
const { type, payload } = e.data || {}; const { type, payload } = e.data || {};
if (type === 'initialize') { if (type === 'initialize') {
this.initialize(payload); this.initialize(payload);
} }
if (type === 'diedACK') {
this.diedACK = true;
}
}; };
this.initialize(); this.initialize();
} }
@ -1338,8 +1357,11 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
this.port.postMessage({ type: 'died' }); this.port.postMessage({ type: 'died' });
this.isAlive = false; this.isAlive = false;
} }
if (this.diedACK || currentTime >= parameters.end[1] + 1) {
return false; return false;
} }
return true;
}
if (currentTime >= parameters.end[0] || currentTime <= parameters.begin[0]) { if (currentTime >= parameters.end[0] || currentTime <= parameters.begin[0]) {
// Inside of grace period or not yet started // Inside of grace period or not yet started
return true; return true;