Boost logo

Boost :

Subject: [boost] [thread] Interaction between interruption points and condition_variable
From: David Stone (david_at_[hidden])
Date: 2017-06-15 22:28:33


Assume

1) A thread is waiting on a condition_variable
2) That condition_variable has been notified that its condition is now true
3) The thread has been interrupted

Given this, the interruption is processed rather than the
condition_variable unblocking normally regardless of the order in which the
notify and the interruption occur. That means that this program would
eventually fail even though the notify always comes before the interrupt.

#include <boost/thread/condition_variable.hpp>
#include <boost/thread/lock_types.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>

#include <cassert>

struct flag_t {
    using lock_type = boost::unique_lock<boost::mutex>;

    void notify() {
        auto lock = lock_type(m_mutex);
        m_flag = true;
        m_cv.notify_one();
    }

    void wait() {
        auto lock = lock_type(m_mutex);
        m_cv.wait(lock, [=]{ return m_flag; });
    }

private:
    bool m_flag = false;
    boost::mutex m_mutex;
    boost::condition_variable m_cv;
};

struct test_t {
    unsigned value = 0;
    ~test_t() {
        assert(value != 0);
        assert(value != 1);
        assert(value == 2);
    }
};

int main() {
    while (true) {
        flag_t flag;
        test_t test;

        auto thread = boost::thread([&]{
            test.value = 1;
            boost::this_thread::interruption_requested();
            flag.wait();
            test.value = 2;
        });

        flag.notify();
        thread.interrupt();
        thread.join();
    }
}

If we move the `flag.notify()` line before the thread creation, this
program never terminates. This is because
`boost::condition_variable::wait(lock_type &, function)` is defined as only
blocking when the function returns false. The only way this program can
terminate is if `thread` begins execution and executes `flag.wait()` before
`flag.notify()` is called, then `thread.interrupt()` runs before `thread`
is unblocked.

It seems more intuitive to me that if a thread is blocked on a
condition_variable and it is interrupted that it would unblock normally
rather than throwing boost::thread_interrupted. Under that behavior, users
who want the current behavior can always check after they leave a
condition_variable::wait using
`boost::this_thread::interruption_requested()` and throw rather than
processing data. Under the current behavior, I do not believe there is any
way to process the data in a function that does not have access to the
thread object without losing the fact that an interruption was requested.
This would only be possible if we had a function like
`boost::this_thread::interrupt()`, but that does not exist.

I do not know what code (if any) depends on the current behavior of
interruptions always taking precedence over notifications, but it appears
that the current behavior is undocumented. Is there a justification for the
current behavior or is it just the way it happened to be implemented? Is
this something we could change, or at least allow users to implement the
behavior I outlined?


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk