Home > database >  How to pass an std::barrier with a lambda completion function to a named function in C
How to pass an std::barrier with a lambda completion function to a named function in C

Time:01-22

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::jthreads. 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.)

  • Related