Search code examples
c++multithreadingboostcrashinterrupt

Crash when calling interrupt() on a boost::thread, which has another boost::thread


I'm getting a crash when calling interrupt() on an outer boost::thread, which runs an inner boost::thread, which is connected to a thread_guard. It's not crashing when calling join() manually on the inner thread.

Crash:
terminate called after throwing an instance of 'boost::thread_interrupted'

Source:
https://gist.github.com/elsamuko/6e178c37fa2cf8742cb6bf512f2ff866

#include <iostream>
#include <thread>

#include <boost/thread/thread.hpp>
#include <boost/thread/thread_guard.hpp>

#define LOG( A ) std::cout << A << std::endl;

void double_interrupt() {
    boost::thread outer([] {
        boost::thread inner([]{
            while(true) {
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }
        });

        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            LOG("Interrupting inner");

            boost::thread_guard<boost::join_if_joinable> guard(inner);   // crashes
            // inner.join(); // works
        }
    });

    LOG("Interrupting outer");
    outer.interrupt();
    outer.join();
}

int main(int argc, char* argv[]) {
    LOG("Start");

    double_interrupt();

    LOG("End");
    return 0;
}

Compile & Run:
http://coliru.stacked-crooked.com/a/46c512bf9a385fff

I'm running on Ubuntu 18.04. with g++ 7.5.0 and got the latest boost 1.78.0.

I opened this issue on github, too: https://github.com/boostorg/thread/issues/366


Solution

  • I got a solution. The problem was, that the join() of the thread_guard waits for the inner thread with a condition_variable::wait(). condition_variable::wait() itself checks, if it's interruptible and throws an exception.

    The solution is to use a custom thread_guard with disable_interruption:

    #include <iostream>
    #include <thread>
    
    #include <boost/thread.hpp>
    #include <boost/thread/thread_guard.hpp>
    
    #define LOG( A ) std::cout << A << std::endl;
    
    void work() {
        size_t sum = 0;
    
        for(int i = 0; i < 1E7; ++i) { sum += 1; }
    
        LOG("work: " << sum);
    }
    
    // helper struct to interrupt a boost::thread within a boost::thread
    struct non_interruptable_interrupt_and_join_if_joinable {
        template <class Thread>
        void operator()(Thread& t) {
            if(t.joinable()) {
                boost::this_thread::disable_interruption di;
                t.interrupt();
                t.join();
            }
        }
    };
    
    void double_interrupt() {
        boost::thread outer([] {
            boost::thread inner([] {
                while(true) {
                    boost::this_thread::interruption_point();
                    work();
                }
            });
            {
                boost::thread_guard<non_interruptable_interrupt_and_join_if_joinable> guard(inner);
                LOG("Interrupting inner");
            }
        });
        LOG("Interrupting outer");
        outer.interrupt();
        outer.join();
    }
    
    int main() {
        LOG("Start");
        double_interrupt();
        LOG("End");
    }
    

    Run here:
    http://coliru.stacked-crooked.com/a/a365e40a2bd574cc