5

I have a program that creates a thread that listens for an event. There is a situation in which this thread will never receive this event, and I must terminate it. I know how to catch the case in which it will not receive this event.

I've created a std::thread, however I found nothing on how to terminate the thread. I've tried calling

t.detach()

And then letting the destructor so the work, but I cannot clean up resources allocated by the thread this way.

My questions are: 1. Is there a way to send a SIGKILL or equivalent to a std::thread to kill it? And 2. Is there a way to catch this signal from within the thread to clean up resources?

The question asked (and answered) How do I terminate a thread in C++11? doesn't answer my question as I want to terminate the thread from it's parent, not from within the thread.

Furthermore, my problem is that my worker thread can be either waiting for a blocking system call (i.e., an incoming packet) or a conditional variable, which is why this is tricky.

Blue Granny
  • 772
  • 1
  • 8
  • 24
  • Threads should see global variables. You can try to use them. – Daniel Nudelman Sep 09 '19 at 13:38
  • 2
    Possible duplicate of [How do I terminate a thread in C++11?](https://stackoverflow.com/questions/12207684/how-do-i-terminate-a-thread-in-c11) – Weak to Enuma Elish Sep 09 '19 at 13:38
  • 4
    The easiest way to kill a thread waiting for events is to create a "You're done" event and inject it so the thread receives as a normal event. You don't really want to try sending signals to specific threads - that can get really nasty if you're doing things like using mutexes in the target thread that need to survive past the thread's lifetime - if you kill a thread holding a mutex, cleaning it up properly is somewhere between difficult-to-do-right and impossible-to-do-at-all depending on your design. – Andrew Henle Sep 09 '19 at 13:50
  • It's not very clear what you are asking for - normally you do not kill a thread manually, but just let it finish... – darune Sep 09 '19 at 13:54
  • You should post more of your code and clarify the question. – darune Sep 09 '19 at 13:57
  • @Weak I cannot terminate within the thread, as its waiting for the event. The thread itself doesn't know the event isn't coming, outside the thread I know. darune I'm trying to work out what I can post without getting in trouble... – Blue Granny Sep 09 '19 at 14:11

4 Answers4

3

You can't kill a thread by standard c++ ways and, generally speaking, it's not a desirable behavior. You can use conditional variables to achieve a similar behavior with a "signal system":

#include <mutex>
#include <thread>
#include <atomic>
#include <iostream>

std::mutex m;
std::condition_variable cv;
std::atomic_bool state;

void worker_thread() {
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, [] { //waits for the notify_one "signal"
        return state.load(); //returns false if the waiting should be continued. 
    });
    std::cout << "Finished waiting..." << std::endl;
}

int main() {
    state.store(false); //set initial state
    std::thread worker(worker_thread);

    std::cout << "Start waiting..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2)); //do some work here
    {
        std::lock_guard<std::mutex> lk(m); //avoids cv to get notified before been in wait state
        state.store(true);
        cv.notify_one(); //emit the signal once "state" var is true
    }

    worker.join();
    return 0;
}
João Paulo
  • 6,300
  • 4
  • 51
  • 80
  • OP said that the thread is queue'ed on an event - this answer assumes it's not waiting indeterminate. – darune Sep 09 '19 at 13:49
  • He said "I know how to catch the case", then he can notify. – João Paulo Sep 09 '19 at 13:51
  • 1
    But then he could just let it run to the end - it seems, at best, unclear. – darune Sep 09 '19 at 13:56
  • Ok, agree that he could be doing this. But maybe he can't reach the end of the thread. If he creates a "signals system", he can achieve a similar behavior. Once he hasn't posted the specific situation, where perhaps he could solve this with a `if`, I posted a general solution. – João Paulo Sep 09 '19 at 13:59
  • The thread is waiting for an event, so the event will wake it up. I cannot have the thread go to sleep on a condition. – Blue Granny Sep 09 '19 at 14:08
  • 1
    This condition could be your event. The effect would be the same. You really don't need to kill your thread if you can control what it does. Condition variables are sufficient. – João Paulo Sep 09 '19 at 14:14
3
  1. Is there a way to send a SIGKILL or equivalent to a std::thread to kill it?

There is no such equivalent; at least a standard one.

  1. Is there a way to catch this signal from within the thread to clean up resources?

There is no standard way in C++ to signal a thread.

POSIX has pthread_kill that can be used to signal a thread. Note that "stop", "continue", or "terminate" signals affect the entire process, so this is not for what you're asking exactly.

However, asynchronous signal handlers are limited on what they can do, and resource cleanup won't be possible. What you should do is let the thread know that it should terminate, and let it stop and cleanup voluntarily.

thread that listens for an event.

Solution depends on what kind of listening we are considering. If it is a condition variable, you can set an atomic boolean to request termination and wake up the thread by notifying.

If listening is a blocking system call, solution is a bit trickier. The great thing about raising a signal, is that it interrupts blocking system calls. You can use the handler to set a volatile sig_atomic_t variable that the thread can read and voluntarily return. The interruption may give your thread a chance to check the variable before resuming the wait. The caveat is that we must turn that may into a guarantee:

You must register the signal handler using sigaction (standard POSIX function; not standard C++) with SA_RESTART unset. Otherwise depending on system defaults, the system call might resume instead of returning.


Another approach is to send the event that the other thread is waiting for. This may be simpler or trickier than above depending on what kind of event we are considering. For example, when listening on a socket, it is typically possible to connect to that socket.


There is a proposal to introduce std::jthread thread wrapper into a future C++ standard, which offers a portable way to request a thread to stop. Unfortunately, I don't know what guarantees there would be regarding blocking system calls.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I do love how *standards* groups taking proposals for new implementation ideas to bandage over the failures of previous proposals adopted by earlier *standards* are discussed without even a hint of irony. No progress, but jobs for life. – mevets Sep 09 '19 at 15:27
  • Thank you for the detailed answer. The event is sometimes a system call and at others times a condition variable. I think you've given me enough to work with, thanks! – Blue Granny Sep 09 '19 at 18:29
  • @ArikRinberg The wait on a condition variable is probably internally a system call, so the signaling approach should work for that case as well. – eerorika Sep 10 '19 at 13:20
  • True, but they are treated differently. I can signal a conditional variable easily. I have to do much more work to send a "kill" packet to the process. – Blue Granny Sep 10 '19 at 16:58
  • @ArikRinberg But the alternative of sendig a signal to the thread, and setting a variable works in both cases (assuming the conditional wait is interrupted like I suspect). – eerorika Sep 10 '19 at 17:01
  • I don't follow your logic. How was signalling a user space conditional variable be able to interrupt a kernel space condition? – Blue Granny Sep 10 '19 at 18:09
  • @ArikRinberg What do you mean by a *kernel space condition* and *user space conditional variable*? – eerorika Sep 10 '19 at 18:14
  • @eerorika Just going off what you said, that condition variable is probably internally a system call. There is a big difference between hanging on a condition variable that I have access to, and hanging on a packet that I don't have control over. – Blue Granny Sep 10 '19 at 18:40
  • @ArikRinberg What is the difference? If you wait for the packet with `listen`, the call will be interrupted by the signal handler. If you wait for a conditiona variable with `std::condition_variable::wait`, that should also be interrupted. It doesn't matter what you're doing as long as the blocking call is interrupted and not resumed. – eerorika Sep 10 '19 at 18:42
  • I believe the method you described that uses EINTR is subject to a race condition: if the signal is received before entering the blocking system call but after checking the flag, then the thread will not exit immediately as desired, and will instead wait for another signal or more input. If you do the flag check just before the blocking call, then this may be a small time window, however it is still non-zero. – PersonWithName Oct 18 '19 at 13:20
  • @hddh good point. I suppose that the approach of sending the waited event is superior in this regard when the event is stored in a queue. – eerorika Oct 18 '19 at 13:31
  • I think I worked out a solution in the case that the thread is blocking on a read() call: before signalling the thread, use fcntl to make the file descriptor non-blocking. You could then also check the flag after the read call. In that case, if the signal arrived in that really awkward time window, it wouldn't be a problem since the read call would immediately return and allow you to check the flag. Depending on the scenario, this may be easier then the self-pipe trick. – PersonWithName Oct 18 '19 at 14:27
  • @hddh Nice idea! But is `fcntl` itself guaranteed to be visible to the other thread? I'm not comfortable enough with the C++ memory model to assume that. Maybe with a `std::atomic_thread_fence` between setting non-blocking and signaling just to be sure. – eerorika Oct 18 '19 at 14:38
  • Considering that fcntl is a system call that modifies flags inside the kernel itself, I would assume there are no memory fence problems there. Once the system call returns, the changes should be applied. – PersonWithName Oct 18 '19 at 14:39
2

Generally killing thread is the undesired behaviour, you want to initiate the thread , do the necessary implementation based on the condition, and if the condition don't satisfy then you could exit it cleanly. Probably use std::condition_variable ... and set the condition from main thread so that child could exit based on certain condition.

2

There's no standard way to kill off a thread, but depends on the platform.

On windows, you may use TerminateThread for example.

However, be advised, unless you know of the sides-effects, etc., this is often-times an anti-pattern to do so.

darune
  • 10,480
  • 2
  • 24
  • 62