Another SO thread explains how to use thread::native_handle to do things that are outside the C++ threading API (e.g., set the priority of a thread). The gist is:
std::thread t(functionToRun);
auto nh = t.native_handle());
// configure t using nh
The problem with this approach is that functionToRun may execute for an arbitrary amount of time (including to completion) before the code to configure t is finished.
I believe we can prevent that from happening as follows (untested):
std::atomic<bool> configured(false);
std::thread t([&]{ while (!configured); // spin until configured is true
functionToRun(); });
auto nh = t.native_handle();
// configure t using nh
configured = true;
Unfortunately, this causes the spawned thread to spin waiting for configured to become true. It’d be preferable for the spawned thread to block until configuration is complete.
One way to accomplish that seems to be to use a mutex (also untested):
std::mutex m;
std::unique_lock<std::mutex> lock(m);
std::thread t([&]{ std::lock_guard<std::mutex> lg(m); // block until m available
functionToRun(); });
auto nh = t.native_handle();
// configure t using nh
lock.unlock(); // allow t to continue
This seems like it should work, but, conceptually, it seems like a condvar is more suited to the job of indicating when a condition (configuration is complete) is satisfied. But using a condvar requires all of the above, plus a condvar, and it would require dealing with the possibility of spurious wakes, which, as far as I know, is not an issue for mutexes.
Is there a better way to spawn a thread and then have it immediately stop so that I can use its native handle to configure it before allowing it to run further?
When I wish to delay the start of a thread’s main function until some external configuration has been completed I use a future. This avoids the spin-wait, and has the same blocking properties as a mutex or condvar, but provides a clearer intention. Using this pattern, you could write your example as:
I especially like using this pattern with
shared_futurefor multithreaded tests — this way you can ensure all threads are running and ready to go before the test begins.