Home > Enterprise >  thread stack access from callback after exit
thread stack access from callback after exit

Time:12-25

The following code has a callback registered for some clean-up actions. As soon as a stop is requested, the callback is invoked from main and the thread exits. The callback then waits a few ms and accesses the vector on the thread stack which seems invalid since the thread no longer exists.

valgrind and g -fsanitize=address do not report any problem and the output of size() is always as expected. This makes me wonder if it is correct to access the vector on the thread stack as long as request_stop in main has not returned.

The output is:

thread finished 140037635319488
call back from: 140037639857984, size 1234
main finished 140037639857984

Note that the vector is accessed after the thread has finished its last line.

Question: is this code undefined behavior?

Some relevant quotes from the C standard might be helpful

#include <iostream>
#include <thread>
#include <vector>
#include <stop_token>
#include <chrono>

using namespace std::chrono_literals;

void thread_func(std::stop_token st)
{
    std::vector<int> v;

    std::stop_callback cb{st, [&v]
        {
            std::this_thread::sleep_for(1000ms);
            for (int i=0; i<1234;   i)
                v.push_back(i);
            std::cout << "call back from: " << std::this_thread::get_id()
                      << ", size " << v.size() << '\n';
        }};

    while (!st.stop_requested())
    {
        // ...
    }
    std::cout << "thread finished " << std::this_thread::get_id() << '\n';
}

int main()
{
    std::stop_source src;
    auto t = src.get_token();

    auto thr = std::jthread{thread_func, t};
    std::this_thread::sleep_for(500ms);

    bool b = src.request_stop();

    std::cout << "main finished " << std::this_thread::get_id() << '\n';
}

CodePudding user response:

If the callback is being run from another thread (in your case the main thread in the request_stop() call), then the destructor of std::stop_callback blocks until that call is finished and destroys the stored callable only afterwards.

Because you declared v before cb, the vector will be destroyed only after the destructor of cb returns during the stack unwinding when thread_func returns after the stop request. The thread only exits after the stack unwinding.

  • Related