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

std::async will hang if no threads are available #8988

Closed
VirtualTim opened this issue Jul 15, 2019 · 11 comments
Closed

std::async will hang if no threads are available #8988

VirtualTim opened this issue Jul 15, 2019 · 11 comments

Comments

@VirtualTim
Copy link
Collaborator

VirtualTim commented Jul 15, 2019

No idea why this is different from regular threads.

#include <thread>
#include <future>
#include <iostream>

int main(int argc, char** argv) {
  //works
  std::thread([] {
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
  }).detach();
  
  //hangs
  std::async([] {
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
  });
}

Compile with: emcc test.cpp -std=c++11 -s USE_PTHREADS=1 -o test.html to see the issue.
Compile with: emcc test.cpp -std=c++11 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.html to make this work as expected.

@blockspacer
Copy link

blockspacer commented Jul 24, 2019

May it be related to

There is an obvious limitation: Don't even think of using async() for tasks that share resources needing locking
**with async() you don't even know how many threads will be used because that's up to async() to decide based on what it knows about the system resources available at the time of a call**.
( from https://stackoverflow.com/questions/26372904/what-limitation-of-stdasync-is-stroustrup-referring-to )

or #8325 ?

@VirtualTim
Copy link
Collaborator Author

That bug was a little different, since the guy was trying to run the whole Emscripten runtime in a worker.
But I'm sure that there are a number of bugs here which relate to the same underlying issue.

@stale
Copy link

stale bot commented Jul 24, 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.

@kleisauke
Copy link
Collaborator

For further readers; note that .detach() does not wait for the thread to finish. Therefore you have to use -s PTHREAD_POOL_SIZE=2 within the above example.

Alternatively, you could try to use .join() instead:

#include <thread>
#include <future>
#include <iostream>

int main() {
    // works
    std::thread([] {
      std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
    }).join();

    // works
    std::async([] {
      std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
    });

    return 0;
}

Compile with:

emcc test.cpp -std=c++11 -s ASSERTIONS=0 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=1 -o test.html

(Tested with Emscripten 2.0.1 without problems)

@VirtualTim
Copy link
Collaborator Author

So the issue described is really about thread spawn with std::async when no threads are available in the pool. You can spawn std::threads in a loop, and if the thread pool is full a new thread will be created. This is not the behaviour with std::async.

So for this example setting PTHREAD_POOL_SIZE=2 avoids the issue, but the underlying behaviour is still there.

@kleisauke
Copy link
Collaborator

kleisauke commented Sep 11, 2020

Ah I see, in that case this SO post is relevant here. Try this:

#include <thread>
#include <future>
#include <iostream>

std::future<void> future;

int main() {
    // works
    std::thread([] {
        std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
    }).detach();

    // works
    auto f = std::async([] {
        std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
    });

    // transfer the future's shared state to a longer-lived future
    future = std::move(f);

    return 0;
}

Compile with:

emcc test.cpp -std=c++11 -s USE_PTHREADS=1 -o test.html

(Tested with Emscripten 2.0.3 without problems)

@VirtualTim
Copy link
Collaborator Author

Ah, thanks for that. I'm not using std::async with Emscirpten any more, but this may come in handy in the future.
I didn't realise the std::async code was actually race-y.

@OlivierSohn
Copy link

@VirtualTim This is kind of related to this issue:

I've been doing some tests in https://github.com/OlivierSohn/async-webassembly and concluded that using explicit std::thread(s) to implement parallelism won't work with WebAssembly / emscriptem.

Using std::thread, I observed deadlocks when more than 3 threads are running at the same time.

Using std::async, there is no such limitation, I could run as many std::async in parallel as I wanted.

@VirtualTim
Copy link
Collaborator Author

That's odd, I'm running a bunch (12) of std::threads in parallel without too many issues.
They are detached threads, and I'm using a fairly old version of Emscripten, and the std::thread implementation has changed a bit since I last looked.
There's a live demo here if you're curious (client-side ecw decoding). Require Chrome/Microsoft Chrome.

@OlivierSohn
Copy link

OlivierSohn commented Apr 8, 2021

@VirtualTim I've done some more tests, and I observe the same behaviour with detached threads.

I've updated my tests (https://github.com/OlivierSohn/async-webassembly) so that instead of having a deadlock, we log an error when not all tasks were able to run at the same time.

The results show that we can start many std::thread(s) in parallel, but they won't be able to run all tasks at the same time. This problem doesn't exist with std::async.

So my guess is that for your application you could get a better level of parallelism using std::async, because more tasks could be running at the same time.

What do you think?

@VirtualTim
Copy link
Collaborator Author

Hm, that's interesting.
I'll have to do some testing myself. I don't think I could easily switch everything over to std::async, as the project I was working on was a port of a legacy C/C++ SDK, and it uses a lot of thread pools using detached std::threads.

One bug I have, that I never managed to track down, was that sometimes on first start the above demo would hang. And it hung hard enough I couldn't even use the dev tools to see what was going on. But it never showed up in the actual product Maybe this is related?

I'm not actively working on this at the moment, the plan is to gauge market sentiment and and browser maturity, but I'll have to look into this when work is started back up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants