Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::thread::join hangs in destructor

Tags:

c++

c++11

I have the following code that runs functions on a dedicated thread. It works great except for the destructor. The call to thread_.join() does not return. I am using VS2013 Express.

What would I change so that the thread joins correctly?

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>

namespace
{
    class main_thread
    {
    public:
        static auto instance() -> main_thread&
        {
            static main_thread instance_;
            return instance_;
        }
        auto enque(std::function<void()> func) -> void
        {
            {
                std::lock_guard<std::mutex> lock{ mutex_ };
                queue_.push_back(func);
            }
            condition_.notify_one();
        }
    private:
        main_thread()
        {
            continue_.test_and_set();
            thread_ = std::thread{ std::bind(std::mem_fn(&main_thread::run), this) };
        }
        ~main_thread()
        {
            continue_.clear();
            condition_.notify_all();
            if (thread_.joinable())
            {
                thread_.join();
            }
        }
        main_thread(const main_thread &other) = delete;
        main_thread(main_thread &&other) = delete;
        main_thread& operator=(const main_thread &other) = delete;
        main_thread& operator=(main_thread &&other) = delete;

        auto run() -> void
        {
            while (continue_.test_and_set())
            {
                auto lock = std::unique_lock<std::mutex>{ mutex_ };
                //condition_.wait_for(lock, std::chrono::milliseconds(1));
                condition_.wait(lock);
                for (auto &func : queue_)
                {
                    func();
                }
                queue_.clear();
            }
        }

        std::condition_variable condition_;
        std::mutex mutex_;
        std::vector<std::function<void()>> queue_;
        std::thread thread_;
        std::atomic_flag continue_;
    };
}

auto on_main_thread(std::function<void()> func) -> void
{
    main_thread::instance().enque(std::move(func));
}

auto on_main_thread_sync(std::function<void()> func) -> void
{
    bool done{ false };
    on_main_thread([&]{
        func();
        done = true;
    });
    while (!done);
}

The only function exercising this code is

int main()
{
    on_main_thread([]{});
}

This avoids the issue of the race in on_main_thread_sync but still has the lockup in ~main_thread. Visual Studio indicates that there are 2 threads, but neither is in main_thread::run, so I do not understand what is going on. That function exited correctly, but it for some reason the thread is not ending.

like image 904
Graznarak Avatar asked Apr 06 '26 22:04

Graznarak


1 Answers

You should not call external code from within a critical section of code, this can easily lead to deadlocks.

If you pause execution in the debugger, you may see that you have one or more threads waiting to acquire _mutex.

You won't be able to acquire the unique_lock on _mutex again if any of the code called from func() tries to enqueue().

Try releasing the lock once the condition_variable wait is over. As a test, you can put in an extra scope to see if this helps:

while (continue_.test_and_set())
{
    std::vector<std::function<void()>> queue;
    {
        auto lock = std::unique_lock<std::mutex>{ mutex_ };
        //condition_.wait_for(lock, std::chrono::milliseconds(1));
        condition_.wait(lock);
        queue.swap(queue_);
    }
    for (auto &func : queue)
    {
        func();
    }
}
like image 176
Phillip Kinkade Avatar answered Apr 09 '26 10:04

Phillip Kinkade



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!