Vuzi Vuzi - 1 month ago 15
C++ Question

C++, signal and threads

I'm having some hard time designing the main workflow of my application, using threads and signals.

My goal here is to have a main thread, catching signals, and n other threads doing stuff periodically (actually using sensors on a raspberry pi, and saving retrieved data). I want the ability to close the program in a clean way, i.e. wait that a sensor has finished writing data (if they are when the signal occur) before closing it. I'm using C++ 11.

For now, I have this example :

#include <iostream>
#include <thread>
#include <csignal>
#include <mutex>

#define NUM_THREAD 3;

static volatile int stop = 0; // If the threads needs to stop
std::mutex m; // Mutex

void threadHandle(int tid) {
while(1) {
std::cout << "Launched by thread " << tid << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "I have sleep well " << tid << std::endl;

m.lock();
if(stop) {
std::cout << "Thread " << tid << " needs to stop" << std::endl;
m.unlock();
break;
}
m.unlock();
}
}

void signalHandler(int signum) {
m.lock();
std::cout << "Signal " << signum << " received" << std::endl;
stop = 1;
m.unlock();
}

int main() {
std::thread t[NUM_THREAD];
std::signal(SIGINT, signalHandler);

//Launch a group of threads
for (int i = 0; i < NUM_THREAD; i++) {
t[i] = std::thread(call_from_thread, i);
}

std::cout << "Launched from the main\n";

for (int i = 0; i < NUM_THREAD; i++) {
t[i].join();
}

return 0;
}


My stop value is volatile only because I'm using it inside a signal handler. Should I use an std::atomic_int to ensure atomic calls to my signal value? Is the mutex really necessary? Or more generally, is this a good way to achieve what I want ?

Another problem I could have, is that some sensors may have a waiting time between measures really long (like 2 or 3 hours), so I would be in need to check the stop value every 2 or 3 seconds.. and it doesn't feel really like the clean way to do it. The other solution, sending signals to the threads, allow me to simply make my thread "sleep"; but again I have no controls to tell the thread to complete its action before terminating.

Thanks in advance !

Answer

Operations on mutex are disallowed in signal handler. You can split stop variable into two:

  1. stop_main, which is set in signal handler, and is waited in the main thread. Because signal handler is executed in the context of the main thread, it is sufficient to declare it volatile (or std::atomic).
  2. stop_threads, which is set in the main thread, and is waited by the other ones. The most suitable type for this variable is std::condition_variable. This type also helps in your waiting for a long time.

Something like this:

std::atomic<int> stop_main = 0;
std::condition_variable stop_threads;
std::mutex m; // protects 'stop_threads'

void threadHandle(int tid)
{
    while(1)
    {
        /* .. do something .. */

        std::unique_lock<std::mutex> l(m);
        if(stop_threads.wait_for(l, std::chrono::hours(2)) ==
            std::cv_status::no_timeout) break;
    }
}
void signalHandler(int signum)
{
    stop_main.store(1);
}
int main()
{
    std::thread t[NUM_THREAD];
    std::signal(SIGINT, signalHandler);

    for (int i = 0; i < NUM_THREAD; i++) {
        t[i] = std::thread(threadHandle, i);


    while(!stop_main.load())
    {
        /* Some workload may be here */
        sleep(10); // Signals will interrupt this function.
    }

    stop_threads.notify_all();

    for (int i = 0; i < NUM_THREAD; i++) {
        t[i].join();

    return 0;
}

I'm uncertain, whether c++ garantees signals delivery to the main thread, not to the others. Otherwise you need to block signals in other threads.

sleep(10) in the main thread may be replaced with any function interruptible by the signals.