Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terminate thread c++11 blocked on read

Tags:

c++

c++11

I've got the following code:

class Foo {
private:
    std::thread thread;
    void run();
    std::atomic_flag running;
    std::thread::native_handle_type native;
public:
    Foo(const std::string& filename);
    virtual ~Foo();
    virtual void doOnChange();
    void start();
    void quit();
};

#include "Foo.h"
#include <functional>

#include <iostream>

Foo::Foo(const std::string& filename) :
        thread(), running(ATOMIC_FLAG_INIT) {
    file = filename;
    native = 0;
}

Foo::~Foo() {
    quit();
}

void Foo::start() {
    running.test_and_set();
    try {
        thread = std::thread(&Foo::run, this);
    } catch (...) {
        running.clear();
        throw;
    }
    native = thread.native_handle();
}

void Foo::quit() {
    running.clear();
    pthread_cancel(native);
    pthread_join(native, nullptr);
    //c++11-style not working here
    /*if (thread.joinable()) {
        thread.join();
        thread.detach();
    }*/
}

void Foo::run() {
   while (running.test_and_set()) {
        numRead = read(fd, buf, BUF_LEN);
        .....bla bla bla.......
   }
}

I'm trying to quit from this thread in my program cleanup code. Using pthread works but I'm wondering if I can do something better with c++11 only (no native handle). It seems to me there's no good way to handle all cases using c++11 code. As you can see here the thread is blocked on a read system call. So even if I clear the flag the thread will be still blocked and join call will block forever. So what I really need is an interrupt (in this case pthread_cancel). But if I call pthread_cancel I can't call anymore the c++11 join() method because it fails, I can only call pthread_join(). So it seems the standard has a really big limitation, am I miss anything?

Edit:

After discussion below I changed the Foo class implementation replacing std::atomic_flag with std::atomic and using signal handler. I used the signal handler because in my opinion is better to have a general base class, using the self-pipe trick is too hard in a base class, the logic should be delegated to the child. Final implementation:

#include <thread>
#include <atomic>

class Foo {
private:
    std::thread thread;
    void mainFoo();
    std::atomic<bool> running;
    std::string name;
    std::thread::native_handle_type native;
    static void signalHandler(int signal);
    void run();
public:
    Thread(const std::string& name);
    virtual ~Thread();
    void start();
    void quit();
    void interrupt();
    void join();
    void detach();
    const std::string& getName() const;
    bool isRunning() const;
};

Cpp file:

#include <functional>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <Foo.h>
#include <csignal>
#include <iostream>

Foo::Foo(const std::string& name) :
        name(name) {
    running = false;
    native = 0;
    this->name.resize(16, '\0');
}

Foo::~Foo() {
}

void Foo::start() {
    running = true;
    try {
        thread = std::thread(&Foo::mainFoo, this);
    } catch (...) {
        running = false;
        throw;
    }
    native = thread.native_handle();
    pthread_setname_np(native, name.c_str());
}

void Foo::quit() {
    if (running) {
        running = false;
        pthread_kill(native, SIGINT);
        if (thread.joinable()) {
            thread.join();
        }
    }
}

void Foo::mainFoo() {
 //enforce POSIX semantics
 siginterrupt(SIGINT, true);
 std::signal(SIGINT, signalHandler);
    run();
    running = false;
}

void Foo::join() {
    if (thread.joinable())
        thread.join();
}

void Foo::signalHandler(int signal) {
}

void Foo::interrupt() {
    pthread_kill(native, SIGINT);
}

void Foo::detach() {
    if (thread.joinable())
        thread.detach();
}

const std::string& Foo::getName() const {
    return name;
}

bool Foo::isRunning() const {
    return running;
}

void Foo::run() {
    while(isRunning()) {
         num = read(.....);
         //if read is interrupted loop again, this time
         //isRunning() will return false
    }
}
like image 422
greywolf82 Avatar asked Aug 08 '18 08:08

greywolf82


People also ask

How do I stop a blocked thread?

interrupt() will not interrupt a thread blocked on a socket. You can try to call Thread. stop() or Thread.

How do you terminate a thread in C++?

The C++11 does not have direct method to terminate the threads. The std::future<void> can be used to the thread, and it should exit when value in future is available. If we want to send a signal to the thread, but does not send the actual value, we can pass void type object.

What does it mean when a thread is blocked?

A Blocked state will occur whenever a thread tries to acquire lock on object and some other thread is already holding the lock. Once other threads have left and its this thread chance, it moves to Runnable state after that it is eligible pick up work based on JVM threading mechanism and moves to run state.

How do you stop a blocking thread in Python?

sleep(0.1) if __name__ == "__main__": thread = BlockingTestThread() thread. start() _async_raise(thread. ident, SystemExit) print "Joining thread" thread. join() print "Done Joining thread" #will never get here!


2 Answers

As you can see here the thread is blocked on a read system call. So even if I clear the flag the thread will be still blocked and join call will block forever.

The solution to this is to std::raise a signal such as SIGINT Edit: You need to raise the signal using pthread_kill so that the signal will be handled by the correct thread. As you can read from the manual, read is interrupted by signals. You must handle the std::signal or else the entire process will terminate prematurely.

On systems that use BSD signal handling instead of POSIX, system calls are by default restarted rather than failed upon interrupt. My suggested approach relies on the POSIX behaviour, where the call sets EINTR and returns. The POSIX behaviour can set explicitly using siginterrupt. Another option is to register the signal handler using sigaction, which does not restart, unless specified by a flag.

After read has been interrupted, you must check whether the thread should stop before retrying the read.

using c++11 (maybe even without it) don't call any blocking system call in the thread

Calling blocking system calls is just fine. What you shouldn't do is call uninterruptible system calls that may block indefinitely long time, if you wish to terminate the thread without terminating the process (within finite time). Off the top of my head, I don't know if any system call matches such description.

A minimal example (complete except for indefinitely blocking read. You can use sleep(100000) to simulate it):

#include <thread>
#include <iostream>
#include <csignal>
#include <cerrno>
#include <unistd.h>

constexpr int quit_signal = SIGINT;
thread_local volatile std::sig_atomic_t quit = false;

int main()
{
    // enforce POSIX semantics
    siginterrupt(quit_signal, true);

    // register signal handler
    std::signal(quit_signal, [](int) {
        quit = true;
    });

    auto t = std::thread([]() {
        char buf[10];
        while(!quit) {
            std::cout << "initiated read\n";
            int count = read(some_fd_that_never_finishes, buf, sizeof buf);
            if (count == -1) {
                if (errno == EINTR) {
                    std::cout << "read was interrupted due to a signal.\n";
                    continue;
                }
            }
        }
        std::cout << "quit is true. Exiting\n";;
    });

    // wait for a while and let the child thread initiate read
    sleep(1);

    // send signal to thread
    pthread_kill(t.native_handle(), quit_signal);

    t.join();
}

Forcibly killing a thread is usually a very bad idea, especially in C++, which is probably why std::thread API doesn't provide interface for it.

If you really want to kill a thread of execution - which isn't necessary in this case, since you can safely interrupt the system call instead - then you should use a child process instead of child thread. Killing a child process won't break the heap of the parent process. That said, C++ standard library does not provide an inter-process API.

like image 74
eerorika Avatar answered Oct 12 '22 15:10

eerorika


As others have said, killing a running thread is a Bad Idea™.

However, in this case, you somehow know the thread is blocking on a read, and want it to stop.

A simple way of doing this is to use the "self pipe trick". Open a pipe, and have the thread block on a select() or poll() call, checking the read end of the pipe and the file descriptor being read. When you want the thread to stop, write a single byte to the write descriptor. The thread wakes up, sees the byte on the pipe and can then terminate.

This approaches avoids the undefined behaviour of killing a thread outright, allows you to use a blocking system call to avoid polling and is responsive to a termination request.

like image 33
janm Avatar answered Oct 12 '22 15:10

janm