From 8a2fc4d5488110ce3ce556ae290b096c1e47f6e8 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 29 Oct 2023 15:16:14 -0700 Subject: [PATCH] js: Properly manage workers when useWorkers is called twice Before this change, calling useWorkers twice would allocate new workers but keep old workers around; this resulted in old workers leaking when the new count was larger; when the new count was smaller, we would still maintain the worker count but recreate some workers and leak older ones. Instead we now only create more workers if more workers are requested, and if the requested count is smaller than current worker count, we ask some existing workers (nicely) to close by sending a dummy message. Because messages are processed in order, and processing the worker response does not access global workers[], it should be safe to call useWorkers(0) at any point without disrupting already dispatched requests. This also provides a clean way to reclaim all resources used by the workers, including WebAssembly memory. --- js/meshopt_decoder.js | 16 ++++++++++++---- js/meshopt_decoder.module.js | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/js/meshopt_decoder.js b/js/meshopt_decoder.js index 748bd49bd..8ff55c38e 100644 --- a/js/meshopt_decoder.js +++ b/js/meshopt_decoder.js @@ -87,7 +87,6 @@ var MeshoptDecoder = (function() { worker.pending -= data.count; worker.requests[data.id][data.action](data.value); - delete worker.requests[data.id]; }; @@ -103,10 +102,16 @@ var MeshoptDecoder = (function() { var blob = new Blob([source], {type: 'text/javascript'}); var url = URL.createObjectURL(blob); - for (var i = 0; i < count; ++i) { + for (var i = workers.length; i < count; ++i) { workers[i] = createWorker(url); } + for (var i = count; i < workers.length; ++i) { + workers[i].object.postMessage({}); + } + + workers.length = count; + URL.revokeObjectURL(url); } @@ -121,7 +126,7 @@ var MeshoptDecoder = (function() { return new Promise(function (resolve, reject) { var data = new Uint8Array(source); - var id = requestId++; + var id = ++requestId; worker.pending += count; worker.requests[id] = { resolve: resolve, reject: reject }; @@ -130,8 +135,11 @@ var MeshoptDecoder = (function() { } function workerProcess(event) { + var data = event.data; + if (!data.id) { + return self.close(); + } self.ready.then(function(instance) { - var data = event.data; try { var target = new Uint8Array(data.count * data.size); decode(instance, instance.exports[data.mode], target, data.count, data.size, data.source, instance.exports[data.filter]); diff --git a/js/meshopt_decoder.module.js b/js/meshopt_decoder.module.js index beea6957f..7e6b72260 100644 --- a/js/meshopt_decoder.module.js +++ b/js/meshopt_decoder.module.js @@ -87,7 +87,6 @@ var MeshoptDecoder = (function() { worker.pending -= data.count; worker.requests[data.id][data.action](data.value); - delete worker.requests[data.id]; }; @@ -103,10 +102,16 @@ var MeshoptDecoder = (function() { var blob = new Blob([source], {type: 'text/javascript'}); var url = URL.createObjectURL(blob); - for (var i = 0; i < count; ++i) { + for (var i = workers.length; i < count; ++i) { workers[i] = createWorker(url); } + for (var i = count; i < workers.length; ++i) { + workers[i].object.postMessage({}); + } + + workers.length = count; + URL.revokeObjectURL(url); } @@ -121,7 +126,7 @@ var MeshoptDecoder = (function() { return new Promise(function (resolve, reject) { var data = new Uint8Array(source); - var id = requestId++; + var id = ++requestId; worker.pending += count; worker.requests[id] = { resolve: resolve, reject: reject }; @@ -130,8 +135,11 @@ var MeshoptDecoder = (function() { } function workerProcess(event) { + var data = event.data; + if (!data.id) { + return self.close(); + } self.ready.then(function(instance) { - var data = event.data; try { var target = new Uint8Array(data.count * data.size); decode(instance, instance.exports[data.mode], target, data.count, data.size, data.source, instance.exports[data.filter]);