Reputation: 101
In C++20 std::jthread
was introduced as a safer version of std::thread
; where std::jthread
, as far as I understand, cleans up after itself when the thread exits.
Also, the concept of cooperative cancellation is introduced such that an std::jthread
manages an std::stop_source
that handles the state of the underlying thread, this std::stop_source
exposes an std::stop_token
that outsiders can use to read the state of the thread sanely.
What I have is something like this.
class foo {
std::stop_token stok;
std::stop_source ssource;
public:
void start_foo() {
// ...
auto calculation = [this](std::stop_token inner_tok) {
// ... (*this is used here)
while(!inner_tok.stop_requested()) {
// stuff
}
}
auto thread = std::jthread(calculation);
ctok = thread.get_stop_token();
ssource = thread.get_stop_source();
thread.detach(); // ??
}
void stop_foo() {
if (ssource.stop_possible()) {
ssource.request_stop();
}
}
~foo() {
stop_foo();
}
}
Note foo
is managed by a std::shared_ptr
, and there is no public constructor.
Somewhere along the line, another thread can call foo::stop_foo()
on a possibly detached thread.
Is what I am doing safe?
Also, when detaching a thread, the C++ handle is no longer associated with the running thread, and the OS manages it, but does the thread keep receiving stop notifications from the std::stop_source
?
Is there a better way to achieve what I need? In MVSC, this doesn't seem to raise any exceptions or halt program execution, and I've done a lot of testing to verify this.
So, is this solution portable?
Upvotes: 4
Views: 2575
Reputation: 4774
What you wrote is potentially unsafe if the thread accesses this
after the foo
has been destroyed. It's also a bit convoluted. A simpler approach would just be to stick the jthread in the structure...
class foo {
std::jthread thr;
public:
void start_foo() {
// ...
jthr = std::jthread([this](std::stop_token inner_tok) {
// ... (*this is used here)
while(!inner_tok.stop_requested()) {
// stuff
}
});
}
void stop_foo() {
jthr.request_stop();
}
~foo() {
stop_foo();
// jthr.detatch(); // this is a bad idea
}
}
To match the semantics of your code, you would uncomment the jthr.detach()
in the destructor, but this is actually a bad idea since then you could end up destroying foo
while the thread is still accessing it. The code I wrote above is safe, but obviously whichever thread drops the last reference to the foo
will have to wait for the jthread
to exit. If that's really intolerable, then maybe you want to change the API to stick a shared_ptr
in the thread itself, so that the thread can destroy foo
if it is still running after the last external reference is dropped.
Upvotes: 3