简体   繁体   中英

I'm trying to used std::signal to cleanly end my multithreaded program, what am I doing wrong?

What I'm trying to do

I have various things that must run concurrently on a linux, until the program is told to stop through ctrl-C (in which case SIGINT is received) or the service is stopped (in which case SIGTERM is received)

What I've come up with

For each thing that need to be done concurrently, I have a class that launches a thread in the constructor and whose destructor makes the thread stop and joins it. It looks basically like this:

#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <system_error>

class SomeClassToDoStuff
{
public:

    // Constructor
    SomeClassToDoStuff()
    {
        _thread = std::thread([this]() {
            while (true)
            {
                // Do some stuff
                ...

                // Before going on to the next iteration
                {
                    std::unique_lock<std::mutex> dataLock(_mutex);

                    // Wait for 2ms
                    if (!_shouldStopThreadExecution)
                    {
                        _conditionVariable.wait_for(dataLock, std::chrono::milliseconds(2));
                    }

                    // End the loop if requested
                    if (_shouldStopThreadExecution)
                    {
                        break;
                    }
                }
            }

            // Some cleanup
            ...
        });
    }

    // Destructor
    ~SomeClassToDoStuff()
    {
        if (_thread.joinable())
        {
            {
                std::lock_guard<std::mutex> dataLock(_mutex);
                _shouldStopThreadExecution = true;
            }

            _conditionVariable.notify_all();

            try
            {
                _thread.join();
            } catch (std::system_error const&) {}
        }
    }

private:
    mutable std::mutex _mutex;                  // Mutex to keep things thread-safe
    std::condition_variable _conditionVariable; // Condition variable used to wait
    std::thread _thread;                        // Thread in which to do the stuff
    bool _shouldStopThreadExecution = false;    // Whether to stop the thread's execution
};

Then my main() looks like this:

#include <atomic>
#include <chrono>
#include <csignal>
#include <iostream>
#include <thread>

namespace  {

std::atomic<int> programReturnValue(-1);  // If positive or zero, the program must return with that value

}

static void signalHandler(int sig)
{
    std::cout << "Signal received (" << sig << "). This iteration of the main loop will be the last." << std::endl;
    programReturnValue.store(0);
}

int main()
{
    // Make sure the program stops cleanly when receiving SIGTERM or SIGINT
    {
        std::signal(SIGTERM, signalHandler);
        std::signal(SIGINT, signalHandler);
    }

    SomeClassToDoStuffA a;
    SomeClassToDoStuffB b;
    SomeClassToDoStuffC c;
    SomeClassToDoStuffD d;

    while (programReturnValue.load() < 0)
    {
        // Check that everything is alright
        if (someCondition)
        {
            programReturnValue.store(1);
        }

        // Wait for 100us to avoid taking all of the processor's resources
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }

    return programReturnValue.load();
}

(By the way, if there's an easier way to go about all this I'm interested to know)

The issue

When I hit ctrl+C or end the service, the program prints that signal 2 or 15 has been received (depending on which I used), and the program ends, which is good. However:

  1. The cleanup involves writing something to a file (in which things are successfully written during execution), but it seems that that doesn't always happen, which means that the cleanup isn't fully performed all the time, and that is a problem
  2. The return code of the program isn't 0 as expected, or even 1, but either 130 or 143 depending on what signal is received

Why is that, and what am I doing wrong?

Edit: From what I understand, 130 and 143 are actually 128 + signal, ie what the program would return if I didn't try to handle the signals

Edit2: I'm getting a better idea of what's happening, and only half the issue seems to be coming from my program itself.

The program is actually run by a bash script, which then prints its return value and may relaunch it depending on the situation. Sending SIGINT and SIGTERM to the script is also supposed to send SIGTERM to the program.

It turns out that I suck at bash. I had written something like this:

#!/bin/sh

trap "killall myProgram --quiet --wait" 2 15

/path/to/myProgram&
wait $!
RETURN_VALUE=$?
echo "Code exited with return code ${RETURN_VALUE}"

# Some other stuff
...
  • ctrl-C while running the script in terminal actually leads to the program receiving both SIGINT then SIGTERM
  • the return code I'm printing is actually the result of wait + trap , not my program's

I will rework the script, but can the fact that both signals are sent to my program the reason why the cleanup fails sometimes? How? What can I do about it?

I am a bit confused about your signal handling:

To me it seems you use the terminating System-signal only to set the return-value and break the while loop in main; the threads, or rather their wrappers are terminated ie destructed only at the time them going out of scope, which is at the end of your main-scope, when you already have returned. Thrown exceptions (in your destructors) cannot be caught anymore, your threads are therefor not ended yet. while you have already returned from main.

As a solution: I would recommend to set the stopping state _shouldStopThreadExecution at the time the main receives the signal for stopping already. And then remove the try statements for the .join() in your destructor in order to see the correct ending of the threads under quaranty.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM