Hello,
C++20 added std::jthread.
One of the differences compared to std::thread
is, that std::jthread can
provide a std::stop_token to
the new thread of execution.
Example:
auto f = [](std::stop_token
stoken) { //stoken is a std::stop_token, associated to our
std::jthread thr1
while (!stoken.stop_requested()) {
// do some work...
}
};
std::jthread thr1(f);
We construct a std::jthread thr1,
which internally holds a std::stop_source
and passes an associated std::stop_token
to a copy of our lambda expression f.
So far, so good. std::jthread
uses is_invocable_v<decay_t<F>,
stop_token, decay_t<Args>...> to determine if the
callable F can be
invoked with a std::stop_token.
Unfortunately, this leads to the effect, that following code
will not compile:
struct T {
void ThreadFunc(std::stop_token stoken) { }
};
int main() {
T obj;
std::jthread thr(&T::ThreadFunc, &obj); // error
here
}
The issue is, that the std::stop_token
is passed to INVOKE before the object-pointer &obj. So it checks if INVOKE(&T::ThreadFunc,
get_stop_token(), &obj) is valid, which it is not. To
make the sample work, a simple workaround could be to write
int main() {
T obj;
std::jthread thr(std::bind_front(&T::ThreadFunc,
&obj)); //Or (since P2714): std::bind_front<&T::ThreadFunc>(&obj)
}
The workaround is fairly simple. Nevertheless, I think that the
current behavior of std::jthread's
constructor is surprising. Personally, I would have expected,
that if I pass a member function pointer to the constructor of std::jthread, that it then
checks for is_invocable_v<decay_t<F>, decay_t<Arg0>, stop_token, decay_t<OtherArgs>...>.
This idea has already been discussed here:
https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg690638.html
. As a result, it got implemented as an extension to libstdc++
(https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/thread#L246).
I am almost sure it would be worth standardizing this extension.
But I first want to float the idea here and collect some
feedback.
Thanks and best regards,
Kilian