minify icon indicating copy to clipboard operation
minify copied to clipboard

JS binding segfaults in worker thread

Open tdewolff opened this issue 2 years ago • 11 comments

From #379: the worker test case fails due to a race condition. Even when calling an empty function in Go it fails. It gets stuck at worker.terminate() when calling runtime.GC() in the clean up function. Looks like something we need to take up with Go or with Node.

Ah no, the os.Exit is terminating the whole node process: https://github.com/dotcore64/rollup-plugin-tdewolff-minify/runs/7272307346?check_suite_focus=true It is exiting after the first test...

tdewolff avatar Jul 10 '22 19:07 tdewolff

See #517

tdewolff avatar Jul 10 '22 19:07 tdewolff

Yeah I completely agree with you, I was thinking that maybe the minifier may have some memory that need cleaning up internally, but I was also fearing that the go runtime has internal memory used by its runtime... since you mention that calling an empty function results in a segfault as well, it seems option number 2 is correct ><;; Might be worth to try out tinygo here? I don't know much about golang at all, just throwing out ideas ><;; btw, I think it might be best to remove the os.Exit(1) for now, since that will have adverse effects at this point :)

perrin4869 avatar Jul 11 '22 02:07 perrin4869

It feels really bad after all the hard work on the C++ bindings, but maybe worth it to look into wasm bindings? https://www.npmjs.com/package/golang-wasm

perrin4869 avatar Jul 11 '22 02:07 perrin4869

Maybe related: https://tinygo.org/docs/guides/webassembly/

perrin4869 avatar Jul 11 '22 10:07 perrin4869

Hm going the WASM way would be possible but I'm unsure about the implications.

I've added a sleep function to avoid races, it seems to work locally, can you verify?

tdewolff avatar Jul 11 '22 23:07 tdewolff

Doesn't seem to help much, or only sometimes. I'm out of ideas, but honestly this is not the only library that fails with worker threads. Is this a common way to use NAPI libraries? How important is supporting worker threads?

tdewolff avatar Jul 12 '22 00:07 tdewolff

I think it would be pretty common. NAPI libraries are usually used to perform CPU heavy tasks, and it's also critical to run them in separate threads to avoid blocking the event loop. Also it enables running this library in parallel...

perrin4869 avatar Jul 12 '22 00:07 perrin4869

But it's ok, it's not urgent to fix this :)

perrin4869 avatar Jul 12 '22 00:07 perrin4869

Hopefully answers to this issue might shed some light: https://github.com/nodejs/node-gyp/issues/2695

tdewolff avatar Jul 12 '22 01:07 tdewolff

This has likely to do with a very similar issue with the Python binding, see https://github.com/tdewolff/minify/issues/535#issuecomment-1256793958

Basically, Go cannot be forked since it will only fork the main thread and not the GC thread. The forked threads from the main thread then hang when trying to garbage collect. For some reason, the fix is quite the inverse: for Python we should load the library in each thread (or use the spawning instead of the forking strategy). For JS we need to load the library globally and call its functions in each thread, apparently the await import of the same library does only initialize it once (the GC thread) but keeps running on different forked threads.

tdewolff avatar Sep 24 '22 00:09 tdewolff

Maybe related to https://github.com/nodejs/node/issues/21783#issuecomment-429637117, see https://github.com/nodejs/node/blob/main/doc/api/addons.md#context-aware-addons and https://napi.inspiredware.com/special-topics/context-awareness.html

tdewolff avatar May 23 '23 00:05 tdewolff