From 39327e2e86886e62f51cb676c8e206876a5732d8 Mon Sep 17 00:00:00 2001 From: Ivan Nikitin Date: Fri, 10 Dec 2021 20:33:43 +0100 Subject: [PATCH] Fixes memory leak in decodeBuffer + destroys worker after 10 seconds of inactivity --- build/build.ts | 4 +++- src/heic2any.ts | 25 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build/build.ts b/build/build.ts index 2904948..9b2ea18 100644 --- a/build/build.ts +++ b/build/build.ts @@ -45,7 +45,9 @@ async function startBuild() { ).replace( /(\\)/g, "\\\\" - )}\n${worker}\n\`;\nvar blob = new Blob([workerString], {type: 'application/javascript'});\nwindow.__heic2any__worker = new Worker(URL.createObjectURL(blob));`; + )}\n${worker}\n\`;` + + "\nwindow.__heic2any__blob = new Blob([workerString], {type: 'application/javascript'});" + + "\nwindow.__heic2any__blob_url = URL.createObjectURL(window.__heic2any__blob);"; console.log("🔨 📄 Fixing main files"); main = worker + main; diff --git a/src/heic2any.ts b/src/heic2any.ts index 28918b4..31572b3 100644 --- a/src/heic2any.ts +++ b/src/heic2any.ts @@ -194,15 +194,36 @@ const utils = { }, }; +function getWorker(): Worker { + if (!(window as any).__heic2any__worker) + (window as any).__heic2any__worker = new Worker((window as any).__heic2any__blob_url); + return (window as any).__heic2any__worker; +} + +let pendingWorkerJobsCount = 0; + +function scheduleWorkerDestruction() { + setTimeout(() => { + if ((window as any).__heic2any__worker && pendingWorkerJobsCount == 0) { + (window as any).__heic2any__worker.terminate(); + delete (window as any).__heic2any__worker; + } + }, 10000); +} + function decodeBuffer(buffer: ArrayBuffer): Promise { return new Promise((resolve, reject) => { const id = (Math.random() * new Date().getTime()).toString(); const message = { id, buffer }; - const worker = (window as any).__heic2any__worker as Worker; + const worker = getWorker(); + pendingWorkerJobsCount++; worker.postMessage(message); - const listener = (message: MessageEvent) => { + const listener = (message: MessageEvent) => { if (message.data.id === id) { worker.removeEventListener("message", listener); + pendingWorkerJobsCount--; + if (pendingWorkerJobsCount == 0) + scheduleWorkerDestruction(); if (message.data.error) reject(message.data.error); else