emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.89k stars 3.32k forks source link

Strange behaviour when detecting ENVIRONMENT_IS_PTHREAD #22541

Open turran opened 2 months ago

turran commented 2 months ago
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.61 (67fa4c16496b157a7fc3377afd69ee0445e8a6e3)
clang version 19.0.0git (https:/github.com/llvm/llvm-project 7cfffe74eeb68fbb3fb9706ac7071f8caeeb6520)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/jl/w/github/gst.wasm/build/gst.wasm_web_wasm32/emsdk/upstream/bin

I am porting a multithreaded library and when creating (and transferring the canvas to) a thread I get the following error:

pthread_create: could not find canvas with ID "#canvas" to transfer to thread!
pthread_create: cannot transfer canvas with ID "canvas" to thread, since the current thread does not have control over it!

My code is creating a new thread from another thread already created. If I use the main thread, everything works as expected. I first thought that it was not possible to create a thread from another thread but there is this code:

      // Synchronously proxy the thread creation to main thread if possible. If we
      // need to transfer ownership of objects, then proxy asynchronously via
      // postMessage.
      if (ENVIRONMENT_IS_PTHREAD && (transferList.length === 0 || error)) {
        return pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg);
      }

Which made me discard that idea, reviewing the .js code I see this:

} else if (!ENVIRONMENT_IS_PTHREAD) {                                                           

            var canvas = (Module['canvas'] && Module['canvas'].id === name) ? Module['canvas'] : document.querySelector(name);
            if (!canvas) {
              err(`pthread_create: could not find canvas with ID "${name}" to transfer to thread!`);      
              error = 28;
              break;
            } 
            if (canvas.controlTransferredOffscreen) {                                                     
              err(`pthread_create: cannot transfer canvas with ID "${name}" to thread, since the current thread does not have control over it!`);
              error = 63; // Operation not permitted, some other thread is accessing the canvas.          
              break;
            }

For some reason, the thread I'm creating the new thread on is evaluating to True in ENVIRONMENT_IS_PTHREAD Any idea what is going on? My understanding is that it should evaluate to False and then recall this proxied from the main thread.

Important topic, this only happens with -sPROXY_TO_PTHREAD set

sbc100 commented 2 months ago

If you are seeing the pthread_create: could not find canvas with ID error message then that means that ENVIRONMENT_IS_PTHREAD is False which means that call has been correctly proxied back to the main thread.

I suppose the question is why isn't Module['canvas'] set correctly here the main thread. Can you set a break point and see if Module['canvas'] exists at all.

Can you share the full set of link flags you are using?

turran commented 2 months ago

Yes, you are right. I read the condition the other way around.

Module['canvas'] is correctly set, the problem is on the second condition canvas.controlTransferredOffscreen, it is true. Like if the canvas has been shared already.

About link flags:

-sERROR_ON_UNDEFINED_SYMBOLS=1 -sWASM_BIGINT -O3 -pthread -fexceptions -sAUTO_JS_LIBRARIES=0 -sAUTO_NATIVE_LIBRARIES=0 -sPTHREAD_POOL_SIZE=32 -sINITIAL_MEMORY=536870912 -lembind -sASYNCIFY -sPROXY_TO_PTHREAD -sFETCH=1 -g3 -lhtml5 -lGL -sOFFSCREENCANVAS_SUPPORT
sbc100 commented 2 months ago

I see, can the canvas only be transfer to one thread? I guess that why PROXY_TO_PTHREAD breaks your use case because likely the canvas has already be transferred to the thread that is running your main function.

It looks like you can override OFFSCREENCANVASES_TO_PTHREAD perhaps? See https://github.com/emscripten-core/emscripten/blob/8800faa6b251e4cf864bf2ebc1ee88cceccba1d1/system/lib/libc/crt1_proxy_main.c#L45-L48 and https://emscripten.org/docs/tools_reference/settings_reference.html#offscreencanvases-to-pthread

turran commented 2 months ago

Hmmm, the situation is similar but not the same. With PROXY_TO_PTHREAD, the following sequence happens if I create my thread requesting (attr set) "#canvas":

Main Thread -> Proxied Thread (the canvas is transferred) -> Thread 2 Creation
                                                          -> Thread 3 Creation with attr

In this case, the canvas is initially transferred to the proxied thread (AFAIK, due to the special 4294967295 value in the code), and it successfully works on thread 3's creation requesting a canvas.

But in this case,

Main Thread -> Proxied Thread (the canvas is transferred) -> Thread 2 Creation -> Thread 3 Creation with attr

It is printing the above errors. It has something to do with the fact that the Thread 3 canvas transferring process it detects that the canvas was already transferred, which is correct, but to the main thread, not thread 2 or any other thread.