Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webassembly worker thread will hang if printf is used in threads. #8325

Closed
overocean opened this issue Mar 22, 2019 · 9 comments
Closed

Webassembly worker thread will hang if printf is used in threads. #8325

overocean opened this issue Mar 22, 2019 · 9 comments
Labels

Comments

@overocean
Copy link

In my c program, if printf is used in a thread, the thread won't print anything and won't finish either.

threadprint.zip

The following is my emcc info.
emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.38.27
clang version 6.0.1 (https://github.com/kripken/emscripten-fastcomp-clang.git 5f5d737f1cfd84e1fcdeca85889b9f85ce7f51fb) (https://github.com/kripken/emscripten-fastcomp.git 9a451c3a09af0fb887413bba5e3e7c340d695ad2) (emscripten 1.38.27 : 1.38.27)
Target: x86_64-pc-windows-msvc
Thread model: posix

@VirtualTim
Copy link
Collaborator

Yeah unfortunately this is a problem/limitation I'm all too familiar with.
So DOM operations can only be done on the main thread. That includes things like printing to the console, and even things like XMLHttpRequests.
So when you print from a thread what actually happens is that this call gets proxied to the main thread.
The issue is that thread.join blocks until the thread operation is complete. But since the thread is waiting on a proxied call to the main thread, and the main thread is waiting on the thread the code deadlocks.

Solutions:

  1. Build with PROXY_TO_PTHREAD. Personally I don't like this one since I have less control on what runs on which threads.
  2. Use thread.detach() instead of thread.join(). Probably fine for your eaxmple, but may not actually work in real situations.
  3. Reactor your code so that the waiting happens on another thread instead of the main thread.
    So change this:
void PrintMessage() {
	printf("message from thread\n");
}
	
EMSCRIPTEN_KEEPALIVE void PrintWithThread() {
	printf("start a thread to print a message\n");
	std::thread thr(PrintMessage);
	printf("thread is running\n");
	thr.join();
	printf("thread is done\n");
}

to something like this:

void PrintMessage() {
	printf("message from thread\n");
}

void PrintMessageManager() {
	printf("thread is running\n");
	std::thread thr(PrintMessage);
	printf("thread is done\n");
	thr.join();
}

EMSCRIPTEN_KEEPALIVE void PrintWithThread() {
	printf("start a thread to print a message\n");
	std::thread thr(PrintMessageManager);
	thr.detach();
}

This is the solution I'm using myself.
Note: detached threads currently don't get returned to the thread pool. There is a PR open, but no one has reviewed it yet #8286 :(

TLDR: Don't use thread.join() on the main thread.

@sbc100
Copy link
Collaborator

sbc100 commented Mar 22, 2019

@VirtualTim is right. I would add that problem is nothing to do with printf. The problem is that the thread itself is not even starting.

You can't start a new thread without yielding the event loop since starting a new threads involves, among other things, postMessage() between the main thread and the new worker.

You might also find -s PTHREAD_POOL_SIZE=1 solves your problem by creating the worker up front.

This issue is common enough and confusing enough that perhaps its worth detecting it and erroring in join.

@overocean
Copy link
Author

Just tried with @VirtualTim's third idea. The PrintMessageManager thread still stuck at the first printf. However, when I run the webassembly module directly in the html, everything works. Even when I changed last line of PrintWithThread to thr.join();.

Here is the new html

<html lang="en" moznomarginboxes mozdisallowselectionprint>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script>
		self.Module = {
			onRuntimeInitialized: function() {
				Module._PrintWithThread();
			}
		};

		</script>
		<script src="PrintLibrary.js"></script>
    </body>
</html>

@VirtualTim
Copy link
Collaborator

I think the issue must be some other interaction then. I can run the following with no problems:

#include <thread>
#include <stdio.h>

void PrintMessage() {
	printf("message from thread\n");
}

void PrintMessageManager() {
	printf("thread is running\n");
	std::thread thr(PrintMessage);
	printf("thread is done\n");
	thr.join();
}

void PrintWithThread() {
	printf("start a thread to print a message\n");
	std::thread thr(PrintMessageManager);
	thr.detach();
}

int main(int argc, char** argv) {
	PrintWithThread();
}

Compile with: emcc PrintWithThread.cpp -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.html

Maybe there's something going on with module workers and threads? Or maybe lto is doing something weird?
If you can create a more minimal example maybe I could take another look at it.

@overocean overocean changed the title Webassebly worker thread will hang if printf is used in threads. Webassembly worker thread will hang if printf is used in threads. Mar 25, 2019
@overocean
Copy link
Author

This example works because there is no web worker involved. When we run PrintWithThread with web worker, the issue will surface.

@overocean
Copy link
Author

Just want to clarify that, in my original example, the web worker was created in a separate JavaScript file.

@sbc100
Copy link
Collaborator

sbc100 commented Mar 26, 2019

Can you explain what you mean by "the web worker was created in a separate JavaScript file." Are you saying that you created the worker yourself and loaded the emscripten generated JS into it? If so, I doubt this will work. Normally emscripten takes care of creating any workers it needs. Also I believe that and a lot of code in emscripten relies on the emscripten-generated JS running in the browser main thread, not in a worker.

@overocean
Copy link
Author

Sorry about the confusion, let me try to clarify it again :) In the original example, I first compiled the c files to expose the c function PrintWithThread. Second I created a JavaScript file, named PrintWorker, where I call the PrintWithThread function with Emscripten API. Third I created a html file, in its body I have

<script>
    var myWorker = new Worker('PrintWorker.js');
</script>

I found that if I don't use the Worker('PrintWorker.js') in the html, but call the function directly in the html body, the printfs will all work.

<script>
	self.Module = {
		onRuntimeInitialized: function() {
			Module._PrintWithThread();
		}
	};
</script>
<script src="PrintLibrary.js"></script>

" I believe that and a lot of code in emscripten relies on the emscripten-generated JS running in the browser main thread, not in a worker."
Is this specified in any documentation? If so, why does this restriction exist? Without this support, the main thread could become unresponsive (e.g. proxied filesystem operations), so this seems like something Emscripten should support.

@stale
Copy link

stale bot commented Mar 25, 2020

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

@stale stale bot added the wontfix label Mar 25, 2020
@stale stale bot closed this as completed Apr 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants