I want to kill a std::thread using its thread object?

CPS picture CPS · Dec 15, 2012 · Viewed 56.6k times · Source

Possible Duplicate:
C++0x thread interruption

I am trying to kill/stop a c++ std::thread by using its thread object.

How can we do this?

Answer

Matthieu M. picture Matthieu M. · Dec 15, 2012

@bamboon's answer is good, however I feel this deserves a stronger statement.

Whatever the language you use, your program will acquire and release resources: memory, file descriptors, ... For simple programs that are fired in one shots, leaking resources does not matter much: when the program ends modern OSes automatically take the resources back; however for long-running programs a basic requirement is not to leak resources, or at least not repetitively.

Therefore, you should have been taught from the beginning that when you acquire a resource you will have to ensure it is released at one point:

void foo(int i) {
    int* array = malloc(sizeof(int) * i);

    /* do something */

    free(array);
}

So, ask yourself the question:

  • what happens when I kill the program ?
  • what happens when I kill the thread ?

Well, as we said, when a program ends the OS gathers the resources back, so assuming (and this is some assumption) that you did not acquire a resource on another system OR that this system is well protected against such abuse, no harm, no foul.

However, when you kill a thread, the program still runs, thus the OS does not gather the resources back. You leaked memory, you locked a file for writing that you cannot unlock any longer, ... You shall not kill threads.

Higher level languages have a way to handle this: exceptions. Because programs should be exception safe anyway, Java (for example) will kill a thread by pausing it, throwing an exception at the point of execution, and gently unwind the stack. However there is no such facility in C++, yet.

Is it impossible ? No, obviously not. Actually, you could perfectly reuse the very same idea:

  • encapsulate std::thread, interruptible_thread class will also contain an interrupt flag
  • pass the address of the flag to std::thread when launching it, and store it in a thread-local way
  • instrument your code with check-points where you check whether the interrupt flag is set or not, and when it is throw an exception

That is:

// Synopsis
class interrupt_thread_exception;
class interruptible_thread;
void check_for_interrupt();

// Interrupt exception
class interrupt_thread_exception: public virtual std::exception {
public:
    virtual char const* what() const override { return "interrupt"; }
}; // class interrupt_thread_exception

// Interruptible thread
class interruptible_thread {
public:
    friend void check_for_interrupt();

    template <typename Function, typename... Args>
    interruptible_thread(Function&& fun, Args&&... args):
        _thread([](std::atomic_bool& f, Function&& fun, Args&&... args) {
                    _flag_ref = &f; fun(std::forward<Args>(args)...);
                },
                _flag,
                std::forward<Function>(fun),
                std::forward<Args>(args)...)
    {}

    bool stopping() const { return _flag.load(); }

    void stop() { _flag.store(true); }

private:
    static thread_local std::atomic_bool* _flag_ref = nullptr;

    std::atomic_bool _flag = false;
    std::thread _thread;
}; // class interruptible_thread

// Interruption checker
inline void check_for_interrupt() noexcept(false) {
    if (not interruptible_thread::_flag_ref) { return; }
    if (not interruptible_thread::_flag_ref->load()) { return; }

    throw interrupt_thread_exception();
} // check_for_interrupt

Now you can just sprinkle your threaded code with checks for interrupt at appropriate places.