I am trying to make some working threads for some (quite) intensive signal processing tasks. I use C 20 for the moment to use std::jthread
which I prefer over std::thread
(although this is not restrictive and I don't think is related to my problem).
I would like to pass an std::barrier
to a "worker function" called from some std::jthread
s. If I set as argument in the "worker function" std::barrier<>&
(which creates an std::barrier
without a completion function) everything seems to be OK. I would like to pass an std::barrier
with a custom completion function (a lambda at the moment but it may end up being a named function). I haven't managed to do that and I get compilation errors.
A working (actually it is not even compiling) example to replicate the problem is
#include <thread>
#include <barrier>
int main() {
std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";}); // num of threads and completion function
std::array<std::jthread> threads{};
for (auto& thread : threads)
thread = std::jthread(workerFunction, std::ref(barrier)); // Worker function (see below) and reference to the barrier
return 0;
}
As a "worker" function I have the following dummy (for testing purposes)
void worker(std::stop_token stopToken, std::barrier<void(void)>& barrier) noexcept { // Most probably the issue is here at the std::barrier<void(void)>&
while (!stopToke.stop_requested()) {
// Do some processing and stuff (at the moment just some dummy increments)
barrier.arrive_and_wait();
}
std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
barrier.arrive_and_drop();
}
At the moment I am working on Microsoft Visual Studio 2022 Community Edition (version 17.4.4) and compile (or at least try to) in debug mode.
The error I get is (where I have put some empty lines in between for clarity)
1>C:\Program Files\Microsoft Visual >Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\barrier(76,13): error C2338: static_assert >failed: 'N4861 [thread.barrier.class]/5: is_nothrow_invocable_v<CompletionFunction&> shall be true'
1>C:\Users\Aris\Documents\Development\Perception Neuron\DSP\TestBarrier.cpp(27,16): message >: see reference to class template instantiation 'std::barrier<void (void)>' being compiled
1>C:\Program Files\Microsoft Visual >Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\xmemory(1395,1): error C2207: >'std::_Compressed_pair<_Ty1,_Ty2,false>::_Myval1': a member of a class template cannot acquire a >function type
1>C:\Program Files\Microsoft Visual >Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\barrier(186,44): message : see reference to >class template instantiation >'std::_Compressed_pair<_Completion_function,std::barrier<_Completion_function>::_Counter_t,false>' >being compiled 1> with 1> [ 1> _Completion_function=void (void) 1> ]
If I pass a lambda function to the thread instead, everything seems to work without errors or warnings. For example, completely dropping the worker()
and instead do
#include <thread>
#include <barrier>
int main() {
std::barrier barrier(5, []() noexcept {std::cout << "Completion reached\n\n";}); // num of threads and completion function
std::array<std::jthread> threads{};
for (auto& thread : threads)
thread = std::jthread([&barrier] (std::stop_token stopToken) {
while (!stopToken.stopRequested()) {
// Do the same stuff
barrier.arrive_and_wait();
}
std::cout << "Termination requested for thread " << std::this_thread::get_id() << '\n';
barrier.arrive_and_drop();
});
return 0;
}
compiles and runs with intended behavior (or at least I haven't noticed anything unusual so far).
Any help and clarification would be most welcome.
CodePudding user response:
First, you should pass a pointer-to-function type, not a function type directly, as the template argument for std::barrier
:
std::barrier<void(*)(void)>&
The you also forgot to add noexcept
to the function type void(void)
(aka void()
). noexcept
is part of the type and here relevant because std::barrier
requires a callable that is nothrow-invocable.
std::barrier<void(*)(void) noexcept>&
Because you are passing the barrier object to the function you also need to make sure that the type in main
is the same. CTAD will deduce a different type because each lambda has a different type. So just std::barrier
without template argument list doesn't work there. (The alternative would be to template worker
on a template parameter T
and then take a std::barrier<T>&
as argument.)