Why does Callgrind make atomic load never ending - c++

I wrote a small program that works perfectly fine until it's being dynamically instrumented by Callgrind:
$ g++ -std=c++11 -pthread -g -ggdb -o program.exe program.cpp
$ time valgrind --tool=callgrind ./program.exe
The code:
#include <atomic>
#include <thread>
#include <iostream>
constexpr int CST_TARGET = 10*1000;
std::atomic<bool> g_lock = {false};
std::atomic<bool> g_got_work = {true};
int g_passer = 0;
long long g_total = 0;
void producer() {
while (1) {
while (g_lock.load(std::memory_order_seq_cst));
if (g_passer >= CST_TARGET) {
g_got_work.store(false, std::memory_order_seq_cst);
return;
}
++g_passer;
g_lock.store(true, std::memory_order_seq_cst);
}
}
void consumer() {
while (g_got_work.load(std::memory_order_seq_cst)) {
if (g_lock.load(std::memory_order_seq_cst)) {
g_total += g_passer;
g_lock.store(false, std::memory_order_seq_cst);
}
}
}
int main() {
std::atomic<int> val(0);
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
std::cout << "g_passer = " << g_passer << std::endl;
std::cout << "g_total = " << g_total << std::endl;
return 0;
}
The instrumentation won't end after 10 mins, so I terminated it and had a look at KCachegrind stats. There are hundreds of millions to billions of calls to std::atomic<bool>::load(...).
Any ideas which parts of Callgrind altered the behaviour of atomic calls and failed them? The program itself runs in milliseconds without Callgrind.

Using --fair-sched=yes should solve the problem.

Related

C++11 multi threaded producer/consumer program hangs

I am new to C++11 and using threading features. In the following program, the main thread starts 9 worker threads and pushes data into a queue and then goes to wait for thread termination. I see that the worker threads don't get woken up and the program just hangs.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <chrono>
#include <future>
#include <atomic>
using namespace std::chrono_literals;
std::mutex _rmtx;
std::mutex _wmtx;
std::queue<unsigned long long> dataq;
std::condition_variable _rcv;
std::condition_variable _wcv;
std::atomic_bool termthd;
void thdfunc(const int& num)
{
std::cout << "starting thread#" << num << std::endl;
std::unique_lock<std::mutex> rul(_rmtx);
while (true) {
while(!_rcv.wait_until(rul, std::chrono::steady_clock::now() + 10ms, [] {return !dataq.empty() || termthd.load(); }));
if (termthd.load()) {
std::terminate();
}
std::cout<<"thd#" << num << " : " << dataq.front() <<std::endl;
dataq.pop();
_wcv.notify_one();
}
}
int main()
{
std::vector<std::thread*> thdvec;
std::unique_lock<std::mutex> wul(_rmtx);
unsigned long long data = 0ULL;
termthd.store(false);
for (int i = 0; i < 9; i++) {
thdvec.push_back(new std::thread(thdfunc, i));
}
for ( data = 0ULL; data < 2ULL; data++) {
_wcv.wait_until(wul, std::chrono::steady_clock::now() + 10ms, [&] {return data > 1000000ULL; });
dataq.push(std::ref(data));
_rcv.notify_one();
}
termthd.store(true);
_rcv.notify_all();
//std::this_thread::yield();
for (int i = 0; i < 9; i++) {
thdvec[i]->join();
}
}
I am unable to figure out the problem. How can I make sure the threads get woken up and processes the requests and terminates normally?
This std::unique_lock<std::mutex> wul(_rmtx); will lock the _rmtx mutex until the end of main scope. It's surely an issue, because other threads trying to get the lock on _rmtx will block:
int main()
{
std::vector<std::thread*> thdvec;
std::unique_lock<std::mutex> wul(_rmtx); // <- locking mutex until end of main.
// other threads trying to lock _rmtx will block
unsigned long long data = 0ULL;
// ... rest of the code ...

c++ threading: cv.notify_one() blocks?

I wrote the following structure to implement a simple single producer / multi consumer synchronization. I'm using two integers available_index and consumed_index, access to consumed_index is protected by the condition variable cv. Here's the code:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <thread>
struct ParserSync {
std::mutex worker_lock;
std::condition_variable cv;
int consumed_index = -1;
int available_index = -1;
bool exit_flag = false;
int consume_index() {
int ret = -1;
// get worker_lock
std::unique_lock<std::mutex> w_lock(worker_lock);
// wait for exit_flag or new available index
cv.wait(w_lock, [this] { return exit_flag || available_index > consumed_index; });
if (available_index > consumed_index) {
consumed_index++;
ret = consumed_index;
}
// Unlock mutex and notify another thread
w_lock.unlock();
cv.notify_one();
return ret;
}
void publish_index() {
available_index++;
std::cout << "before" << std::endl;
cv.notify_one();
std::cout << "after" << std::endl;
}
void set_exit() {
exit_flag = true;
cv.notify_all();
}
};
I tested my implementation using the following code (just a simple example to show the problem):
void producer(ParserSync &ps){
for (int i=0;i<5000;i++){
ps.publish_index();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ps.set_exit();
std::cout << "Producer finished!" << std::endl;
}
void consumer(ParserSync &ps){
while (true){
int idx = ps.consume_index();
if (idx == -1)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(4));
}
std::cout << "Consumer finished!" << std::endl;
}
int main() {
ParserSync ps{};
const int num_consumers = 4;
std::vector<std::thread> consumer_threads(num_consumers);
// start consumers
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i] = std::thread{consumer, std::ref(ps)};
}
// start producer
std::thread producer_thread = std::thread{producer, std::ref(ps)};
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i].join();
}
producer_thread.join();
std::cout << "Program finished" << std::endl;
return 0;
}
I would expect that producer thread produces 5000 indices and exits afterwards, but unfortunately, it gets stuck at some random iteration. I used print statements to find the code line that blocks and tracked it down to cv.notify_one();. This is the (shortened) console output:
...
before
after
before
after
before
Does anyone know why the call to cv.notify_one(); blocks?
I'm using MinGW (x86_64-6.2.0-posix-seh-rt_v5-rev1) on Windows 10.
Thanks in advance!
EDIT:
When compiling the exact same code with Visual Studio, the program works as expected and doesn't lock itself up. Unfortunately, I need to use MinGW for other reasons.

Deadlock using std::thread and std::condition_variable

I'm investigating an issue where my worker thread deadlocks when I try to stop it.
Here's the minimal version that has the problem:
#include <atomic>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
class Worker
{
private:
std::atomic<bool> m_running;
std::condition_variable m_cond;
std::mutex m_mutex;
std::function<void()> m_workItem;
std::thread m_thread;
public:
Worker() :
m_running(true),
m_thread(std::bind(&Worker::DoWork, this))
{
}
~Worker()
{
Stop();
m_thread.join();
}
bool QueueWork(std::function<void()> item)
{
std::unique_lock<std::mutex> padlock(m_mutex);
if (m_workItem)
{
return false;
}
m_workItem = item;
padlock.unlock();
m_cond.notify_all();
return true;
}
void Stop()
{
bool expected = true;
if (m_running.compare_exchange_strong(expected, false))
{
m_cond.notify_all();
}
}
private:
void DoWork() noexcept
{
while (m_running)
{
std::unique_lock<std::mutex> padlock(m_mutex);
m_cond.wait(padlock,
[this]
()
{
return !m_running || m_workItem;
});
if (m_workItem)
{
decltype(m_workItem) workItem;
std::swap(m_workItem, workItem);
padlock.unlock();
workItem();
}
}
}
};
int main()
{
std::cout << "Start." << std::endl;
{
std::vector<std::unique_ptr<Worker>> workers;
for (int i = 0; i < 10; ++i)
{
workers.push_back(std::unique_ptr<Worker>(new Worker()));
}
workers[0]->QueueWork(
[]()
{
std::cout << "Work item" << std::endl;
});
std::this_thread::sleep_for(std::chrono::milliseconds(10));
for (auto & worker : workers)
{
worker->Stop();
}
for (auto & worker : workers)
{
worker.reset();
}
}
std::cout << "Stop." << std::endl;
return 0;
}
The idea is that when calling Worker::Stop() from the host thread, Worker::m_running is set to false, and notify_all() is called on Worker::m_cond. The worker thread wakes up from its m_cond.wait(), checks m_running and breaks out, exiting the thread function.
Sometimes, however, this deadlocks. The worker thread wakes up, sees that m_running is true (how is that possible?) and goes back to wait(). There's no additional call to m_cond.notify_all() so the thread ends up in a deadlocked state.
I spawn 10 Worker objects in this code. I don't think it has anything to do with the number of threads, but to be able to trigger the race condition (if that's what it is), I needed more threads.
What is wrong with the code?
Running gcc:
g++ --version
g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Compiling using:
g++ -std=c++11 -pedantic test.cpp -lpthread
Edit:
Changed the order of Worker's private members. m_thread is now last. Still the same problem.

Getting Native Handle from Within a Thread?

I am using VS2012 and I want to set thread priority from within a running thread. The goal is to initialize all threads with the highest priority state. To do this I want to get a HANDLE to the thread.
I am having some trouble accessing the pointer that corresponds to the thread object.
Is this possible?
From the calling main thread, the pointer is valid and from the C++11 thread it is set to CCCCCCCC. Predictably dereferencing some nonsense memory location causes a crash.
The code below is a simplified version showing the problem.
#include "stdafx.h"
#include <Windows.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <atomic>
using namespace std;
class threadContainer
{
thread* mT;
condition_variable* con;
void lockMe()
{
mutex m;
unique_lock<std::mutex> lock(m);
con->wait(lock);//waits for host thread
cout << mT << endl;//CCCCCCCC
auto h = mT->native_handle();//causes a crash
con->wait(lock);//locks forever
}
public:
void run()
{
con = new condition_variable();
mT = new thread(&threadContainer::lockMe,*this);
cout << mT << endl; //00326420
con->notify_one();// Without this line everything locks as expected
mT->join();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
threadContainer mContainer;
mContainer.run();
return 0;
}
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <atomic>
#include <thread>
class threadContainer {
std::thread* mT;
std::mutex m;
void lockMe() {
// wait for mT to be assigned:
{
std::unique_lock<std::mutex> lock(m);
}
std::cout << "lockMe():" << mT << "\n";
auto h = mT->native_handle();//causes a crash
std::cout << "Done lockMe!\n";
}
public:
void run() {
// release lock only after mT assigned:
{
std::unique_lock<std::mutex> lock(m);
mT = new std::thread( [&](){ this->lockMe(); } );
}
std::cout << "run():" << mT << "\n"; //00326420
mT->join();
}
};
int main() {
threadContainer mContainer;
mContainer.run();
return 0;
}
Try that.
0xcccccccc means "variable not initialized". You have a threading race bug in your code. The thread starts running before the "mT" variable is assigned. You will need additional synchronization to block the thread until the assignment is completed so you can safely use mT. This will then also ensure that the new thread can see the updated value of mT, a memory barrier is required on a multi-core machine.
This is an example code with condition_variable and mutex.
class threadContainer
{
std::thread* mT;
std::mutex m;
std::condition_variable cv;
bool flag;
void lockMe() {
// 1. you must acquire lock of mutex.
unique_lock<std::mutex> lk(m);
// 2. and wait on `cv` for `flag==true`
cv.wait(lk, [&]{ return flag; });
cout << mT << endl;
auto h = mT->native_handle();
}
public:
void run()
{
flag = false;
mT = new std::thread( [&](){ this->lockMe(); } );
{
// 3. set `flag` and signal `cv`
lock_guard<decltype(m)> lk(m);
cout << mT << endl;
flag = true;
cv.notify_one();
}
mT->join();
}
};
If what you really want to do is "initialize all threads with the highest priority state", how about this simplified code?
Anyway, changing thread priority is platform dependent and out of C++ Standard library.
class threadContainer
{
std::thread thd;
void work() {
// (1) change thread priority itself
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
// do something...
}
public:
void run()
{
thd = std::thread( [&](){ this->work(); } );
// (2) or change thread priority from outside
::SetThreadPriority(thd.native_handle(), THREAD_PRIORITY_HIGHEST);
thd.join();
}
};

How to check if a std::thread is still running?

How can I check if a std::thread is still running (in a platform independent way)?
It lacks a timed_join() method and joinable() is not meant for that.
I thought of locking a mutex with a std::lock_guard in the thread and using the try_lock() method of the mutex to determine if it is still locked (the thread is running), but it seems unnecessarily complex to me.
Do you know a more elegant method?
Update: To be clear: I want to check if the thread cleanly exited or not. A 'hanging' thread is considered running for this purpose.
If you are willing to make use of C++11 std::async and std::future for running your tasks, then you can utilize the wait_for function of std::future to check if the thread is still running in a neat way like this:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
/* Run some task on new thread. The launch policy std::launch::async
makes sure that the task is run asynchronously on a new thread. */
auto future = std::async(std::launch::async, [] {
std::this_thread::sleep_for(3s);
return 8;
});
// Use wait_for() with zero milliseconds to check thread status.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
auto result = future.get(); // Get result.
}
If you must use std::thread then you can use std::promise to get a future object:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
// Create a promise and get its future.
std::promise<bool> p;
auto future = p.get_future();
// Run some task on a new thread.
std::thread t([&p] {
std::this_thread::sleep_for(3s);
p.set_value(true); // Is done atomically.
});
// Get thread status using wait_for as before.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
t.join(); // Join thread.
}
Both of these examples will output:
Thread still running
This is of course because the thread status is checked before the task is finished.
But then again, it might be simpler to just do it like others have already mentioned:
#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
std::atomic<bool> done(false); // Use an atomic flag.
/* Run some task on a new thread.
Make sure to set the done flag to true when finished. */
std::thread t([&done] {
std::this_thread::sleep_for(3s);
done = true;
});
// Print status.
if (done) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
t.join(); // Join thread.
}
Edit:
There's also the std::packaged_task for use with std::thread for a cleaner solution than using std::promise:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
// Create a packaged_task using some task and get its future.
std::packaged_task<void()> task([] {
std::this_thread::sleep_for(3s);
});
auto future = task.get_future();
// Run task on new thread.
std::thread t(std::move(task));
// Get thread status using wait_for as before.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
// ...
}
t.join(); // Join thread.
}
An easy solution is to have a boolean variable that the thread sets to true on regular intervals, and that is checked and set to false by the thread wanting to know the status. If the variable is false for to long then the thread is no longer considered active.
A more thread-safe way is to have a counter that is increased by the child thread, and the main thread compares the counter to a stored value and if the same after too long time then the child thread is considered not active.
Note however, there is no way in C++11 to actually kill or remove a thread that has hanged.
Edit How to check if a thread has cleanly exited or not: Basically the same technique as described in the first paragraph; Have a boolean variable initialized to false. The last thing the child thread does is set it to true. The main thread can then check that variable, and if true do a join on the child thread without much (if any) blocking.
Edit2 If the thread exits due to an exception, then have two thread "main" functions: The first one have a try-catch inside which it calls the second "real" main thread function. This first main function sets the "have_exited" variable. Something like this:
std::atomic<bool> thread_done = false;
void *thread_function(void *arg)
{
void *res = nullptr;
try
{
res = real_thread_function(arg);
}
catch (...)
{
}
thread_done = true;
return res;
}
This simple mechanism you can use for detecting finishing of a thread without blocking in join method.
std::thread thread([&thread]() {
sleep(3);
thread.detach();
});
while(thread.joinable())
sleep(1);
You can always check if the thread's id is different than std::thread::id() default constructed.
A Running thread has always a genuine associated id.
Try to avoid too much fancy stuff :)
Create a mutex that the running thread and the calling thread both have access to. When the running thread starts it locks the mutex, and when it ends it unlocks the mutex. To check if the thread is still running, the calling thread calls mutex.try_lock(). The return value of that is the status of the thread. (Just make sure to unlock the mutex if the try_lock worked)
One small problem with this, mutex.try_lock() will return false between the time the thread is created, and when it locks the mutex, but this can be avoided using a slightly more complex method.
Surely have a mutex-wrapped variable initialised to false, that the thread sets to true as the last thing it does before exiting. Is that atomic enough for your needs?
I checked both systems:
-Using thread+atomic: take 9738 milliseconds
-Using future+async: take 7746 milliseconds
Not threads: 56000milliseconds
Using a Core-I7 6 cores laptop
My code creates 4000 threads, but no more than 12 running every time.
Here is the code:
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <mutex> // std::mutex
#include <atomic>
#include <chrono>
#pragma warning(disable:4996)
#pragma warning(disable:6031)
#pragma warning(disable:6387)//strout
#pragma warning(disable:26451)
using namespace std;
const bool FLAG_IMPRIME = false;
const int MAX_THREADS = 12;
mutex mtx; // mutex for critical section
atomic <bool> th_end[MAX_THREADS];
atomic <int> tareas_acabadas;
typedef std::chrono::high_resolution_clock t_clock; //SOLO EN WINDOWS
std::chrono::time_point<t_clock> start_time, stop_time; char null_char;
void timer(const char* title = 0, int data_size = 1) { stop_time = t_clock::now(); double us = (double)chrono::duration_cast<chrono::microseconds>(stop_time - start_time).count(); if (title) printf("%s time = %7lgms = %7lg MOPs\n", title, (double)us * 1e-3, (double)data_size / us); start_time = t_clock::now(); }
class c_trim
{
char line[200];
thread th[MAX_THREADS];
double th_result[MAX_THREADS];
int th_index;
double milliseconds_commanded;
void hilo(int hindex,int milliseconds, double& milliseconds2)
{
sprintf(line, "%i:%ia ",hindex, milliseconds); imprime(line);
this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
milliseconds2 = milliseconds * 1000;
sprintf(line, "%i:%ib ", hindex, milliseconds); imprime(line);
tareas_acabadas++; th_end[hindex] = true;
}
int wait_first();
void imprime(char* str) { if (FLAG_IMPRIME) { mtx.lock(); cout << str; mtx.unlock(); } }
public:
void lanzatareas();
vector <future<void>> futures;
int wait_first_future();
void lanzatareas_future();//usa future
};
int main()
{
c_trim trim;
timer();
trim.lanzatareas();
cout << endl;
timer("4000 tareas using THREAD+ATOMIC:", 4000);
trim.lanzatareas_future();
cout << endl;
timer("4000 tareas using FUTURE:", 4000);
cout << endl << "Tareas acabadas:" << tareas_acabadas << endl;
cout << "=== END ===\n"; (void)getchar();
}
void c_trim::lanzatareas()
{
th_index = 0;
tareas_acabadas = 0;
milliseconds_commanded = 0;
double *timeout=new double[MAX_THREADS];
int i;
for (i = 0; i < MAX_THREADS; i++)
{
th_end[i] = true;
th_result[i] = timeout[i] = -1;
}
for (i = 0; i < 4000; i++)
{
int milliseconds = 5 + (i % 10) * 2;
{
int j = wait_first();
if (th[j].joinable())
{
th[j].join();
th_result[j] = timeout[j];
}
milliseconds_commanded += milliseconds;
th_end[j] = false;
th[j] = thread(&c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
}
}
for (int j = 0; j < MAX_THREADS; j++)
if (th[j].joinable())
{
th[j].join();
th_result[j] = timeout[j];
}
delete[] timeout;
cout <<endl<< "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}
void c_trim::lanzatareas_future()
{
futures.clear();
futures.resize(MAX_THREADS);
tareas_acabadas = 0;
milliseconds_commanded = 0;
double* timeout = new double[MAX_THREADS];
int i;
for (i = 0; i < MAX_THREADS; i++)
{
th_result[i] = timeout[i] = -1;
}
for (i = 0; i < 4000; i++)
{
int milliseconds = 5 + (i % 10) * 2;
{
int j;
if (i < MAX_THREADS) j = i;
else
{
j = wait_first_future();
futures[j].get();
th_result[j] = timeout[j];
}
milliseconds_commanded += milliseconds;
futures[j] = std::async(std::launch::async, &c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
}
}
//Last MAX_THREADS:
for (int j = 0; j < MAX_THREADS; j++)
{
futures[j].get();
th_result[j] = timeout[j];
}
delete[] timeout;
cout << endl << "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}
int c_trim::wait_first()
{
int i;
while (1)
for (i = 0; i < MAX_THREADS; i++)
{
if (th_end[i] == true)
{
return i;
}
}
}
//Espera que acabe algun future y da su index
int c_trim::wait_first_future()
{
int i;
std::future_status status;
while (1)
for (i = 0; i < MAX_THREADS; i++)
{
status = futures[i].wait_for(0ms);
if (status == std::future_status::ready)
return i;
}
}
I also had this problem very recently. Tried with the C++20 std::jthread using the shared-stop state to check if the thread is over, but inside the thread the std::stop_token argument is a readonly and doesn't indicate to outside when the thread finishes.
So I created a simple class (nes::uthread) extending std::thread with a flag to indicate it's finished. Example:
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
namespace nes {
class uthread final
{
std::unique_ptr<std::atomic<bool>> m_finished;
std::thread m_thr;
public:
uthread()
: m_finished { std::make_unique<std::atomic<bool>>(true) }
{}
template <class Function, class... Args>
uthread(Function&& f, Args&&... args)
: m_finished { std::make_unique<std::atomic<bool>>(false) }
, m_thr {
[](std::atomic<bool>& finished, Function&& ff, Args&&... aargs) {
try {
std::forward<Function>(ff)(std::forward<Args>(aargs)...);
finished = true;
} catch (...) {
finished = true;
throw;
}
},
std::ref(*m_finished), std::forward<Function>(f),
std::forward<Args>(args)...
}
{}
uthread(const uthread&) = delete;
uthread(uthread&&) = default;
uthread& operator=(const uthread&) = delete;
uthread& operator=(uthread&&) = default;
[[nodiscard]] std::thread::id get_id() const noexcept {
return m_thr.get_id(); }
[[nodiscard]] bool joinable() const noexcept { return m_thr.joinable(); }
void join() { m_thr.join(); }
[[nodiscard]] const std::atomic<bool>& finished() const noexcept {
return *m_finished; }
};
}
int main()
{
using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
using namespace nes;
{
cout << "std::thread join() termination\n";
atomic<bool> finished = false;
thread t { [&finished] {
this_thread::sleep_for(2s);
finished = true;
cout << "thread ended\n";
}};
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << finished << '\n';
this_thread::sleep_for(1s);
}
t.join();
}
cout << '\n';
{
cout << "std::jthread join() termination\n";
jthread t {[](stop_token st) {
this_thread::sleep_for(2s);
cout << "thread ended. stop possible: " << st.stop_possible() << '\n';
}};
auto st = t.get_stop_source();
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << !st.stop_possible() << '\n';
this_thread::sleep_for(1s);
}
}
cout << '\n';
{
cout << "nes::uthread join() termination\n";
uthread t {[] {
this_thread::sleep_for(2s);
cout << "thread ended\n";
}};
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << t.finished() << '\n';
this_thread::sleep_for(1s);
}
t.join();
}
}
Possible prints:
std::thread join() termination
2.join() 1 finished: 0
2.join() 1 finished: 0
thread ended
2.join() 1 finished: 1
2.join() 1 finished: 1
2.join() 1 finished: 1
std::jthread join() termination
3.join() 1 finished: 0
3.join() 1 finished: 0
thread ended. stop possible: 1
3.join() 1 finished: 0
3.join() 1 finished: 0
3.join() 1 finished: 0
nes::uthread join() termination
4.join() 1 finished: 0
4.join() 1 finished: 0
thread ended
4.join() 1 finished: 1
4.join() 1 finished: 1
4.join() 1 finished: 1
You can use std::jthread in nes::uthread so you don't need to join.