Search code examples
javascriptgoogle-chromecrashweb-worker

Chrome crash with webworkers and createImageBitmap


I'm currently having an issue when loading images with webworkers. I want to batch load a bunch of images and then do some processing on these images (in my case, convert source image to ImageBitmap using createImageBitmap). Currently the user has the ability to cancel the request. This causes a crash when trying to terminate the worker if the worker hasn't finished. I've created a fiddle here https://jsfiddle.net/e4wcro0o/18/ that crashes consistently.

The issue lies here:

function closeWorker() {
   if (!isClosed) {
    console.log("terminating worker");
    isClosed = true;
    worker.terminate();
  }
}

for (let i = 0; i < srcImages.length; i++) {
    loadImageWithWorker(new URL(srcImages[i], window.location).toString()).then(function(img) {
    closeWorker();
    console.log(img);
  });
}

This may look a bit funky to call closeWorker() on the first resolved promise, but does it mean that the crash is reproducible. I've only test on chrome with 64.0.3282.186 (Official Build) (64-bit)

Any ideas on what I'm doing wrong?


Solution

  • I have come across the same issue. I think the cause comes terminating the worker during the createImageBitmap function.

    I have modified your JSFiddle with a method of terminating the worker at the earliest chance to avoid a crash.

    const worker = createWorker(() => {
    
        const pendingBitmaps = {};
        var pendingKill = false;
    
        self.addEventListener("message", e => {
    
            const src = e.data;
    
            if (src == "KILL") {
                pendingKill = true;
                Promise.all(Object.values(pendingBitmaps)).then(_ => self.postMessage("READY"));
            }
    
            // not accepting anymore conversions
            if (pendingKill) {
                self.postMessage({src, bitmap: null});
                return;
            }
    
            pendingBitmaps[src] = fetch(src).then(response => response.blob())
                .then(blob => {
                    if (pendingKill) return null;
                    return createImageBitmap(blob);
                })
                .then(bitmap => {
                    self.postMessage({src,bitmap});
                    delete pendingBitmaps[src];
                })
        })
    });
    

    https://jsfiddle.net/wrf1sLbx/16/