34

I am creating multiple threads in my program. On pressing Ctrl-C, a signal handler is called. Inside a signal handler, I have put exit(0) at last. The thing is that sometimes the program terminates safely but the other times, I get runtime error stating

abort() has been called

So what would be the possible solution to avoid the error?

Noam Hacker
  • 4,671
  • 7
  • 34
  • 55
rjlion795
  • 515
  • 1
  • 5
  • 16
  • You "join" the threads created. If you don't and just exit one thread then there may be loose threads still hanging around. – mathreadler Apr 18 '18 at 11:13
  • 14
    Note: in general it is UB to call `exit()` from a signal handler. – psmears Apr 18 '18 at 14:01
  • 1
    I think you can call `_exit()` or `_Exit()` though. – Zan Lynx Apr 18 '18 at 18:06
  • 1
    @mathreadler: I'm not sure this is an ISO C++ requirement, but AFAIK on a modern POSIX system, `exit()` or `_exit()` is required to terminate the entire process, not just the current thread. This is why glibc2.3 (many years ago) changed [`_exit(2)` to use Linux's `sys_exit_group` system call instead of `sys_exit`](https://stackoverflow.com/q/46903180), terminating all threads (without calling any destructors or anything). `exit(3)` on Linux also uses `sys_exit_group`. – Peter Cordes May 16 '18 at 03:59
  • @PeterCordes it is quite likely you are right. I have not coded very many multi-threaded programs. Just fooled around a bit when I was younger. – mathreadler May 16 '18 at 16:26

4 Answers4

32

The usual way is to set an atomic flag (like std::atomic<bool>) which is checked by all threads (including the main thread). If set, then the sub-threads exit, and the main thread starts to join the sub-threads. Then you can exit cleanly.

If you use std::thread for the threads, that's a possible reason for the crashes you have. You must join the thread before the std::thread object is destructed.

Valloric
  • 2,983
  • 2
  • 20
  • 10
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 6
    @Hamed Practically? For a simple `bool` flag then no, not much. Theoretically? Yes it's a data-race. But better be always safe, so you should be using e.g. `std::atomic`. – Some programmer dude Apr 18 '18 at 06:43
  • 5
    @Hamed Helpful additional reading: [Are sig_atomic_t and std::atomic<> interchangable](https://stackoverflow.com/questions/15949036/are-sig-atomic-t-and-stdatomic-interchangable) – user4581301 Apr 18 '18 at 06:47
  • 24
    I'm inclined to say that a non-atomic bool can **definitively** cause problems when used as an exit-thread flag. The chief problem is that threads often check that flag in a loop `while(! exitThread() ) { doWork(); }`. If that's a non-atomic variable, it may end up being hoisted from the loop `if (exitThread) return; while(true) { doWork(); }`. – MSalters Apr 18 '18 at 07:10
  • 4
    @Someprogrammerdude i guess you specifiy bool as practically save, since its such a small type. but atomics are not only important for the atomicity of their operations, its about memory ordering too what makes them nessesary in multithreaded code – phön Apr 18 '18 at 07:10
  • 1
    @BЈовић Yes, but that brings with it *other* problems. If the process exits then the threads will be forcibly killed by the operating system, the threads will not get a chance to clean up and exit gracefully. It works for short-running threads for which a result is not wanted, and which are more or less guaranteed to exit in a clean and timely manner even after the main thread have exited. It also requires a redesign of the main thread, so the process does not exit. – Some programmer dude Apr 18 '18 at 08:11
  • 1
    Besides `std::atomic`, you can use `volatile bool` to avoid compiler optimizations that lift reading the value out of a loop. Reads up to a platform dependent width (8 bytes maybe) are atomic. – Khouri Giordano Apr 18 '18 at 10:04
  • @KhouriGiordano: Seems like a bad idea. Can you prove there are no loopholes? Seems like a lot of work to verify that the standard ensures a (conforming) compiler must emit a program that does what is intended. What benefit do you think you're gaining by doing so? –  Apr 18 '18 at 10:28
  • 2
    @KhouriGiordano I don't think it's a good idea since volatile only guarantees that instructions are not omitted and the instruction ordering is remain the same but, does not guarantee a memory barrier to enforce cache coherence. – HMD Apr 18 '18 at 10:43
  • @Hurkyl Atomic operations are not free. They force all cores to synchronize on that cache line for every read. If it is critical that all cores see the new value immediately, then atomic is the way to go. If atomic is causing a performance hit, volatile may be an acceptable compromise. – Khouri Giordano Apr 18 '18 at 10:59
  • 5
    @KhouriGiordano First, `volatile` does not offer thread-safety. Please stop beating this [dead horse](https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading#4558031). Second, the standard does not state anywhere that "all cores see the new value immediately" for atomics. Relaxed memory order is sufficient here and – in theory – has no direct overhead on e.g. x86-64 unless you use RMW (including CAS). In fact, I just tested it with MSVC and GCC 7.0 on godbolt, and relaxed stores and loads of `bool` are translated to simple `MOV`s. – Arne Vogel Apr 18 '18 at 14:39
  • 3
    @ArneVogel The **HUGE** problem is that in their infinite wisdom, Microsoft added memory barrier semantics to the `volatile` keyword for their X86/X64 compilers. So unfortunately, even though it is not standards compliant, `volatile` will work as (incorrectly) expected in this case under MSVC X86/X64. That said, their own page now explains the exact inner workings, and advises against its use for multi-threaded operation. https://docs.microsoft.com/en-us/cpp/cpp/volatile-cpp To their credit, they also now offer a `/volatile:iso` compiler switch that makes `volatile` work right on X86/X64. – dgnuff Apr 18 '18 at 22:53
18

Others have mentioned having the signal-handler set a std::atomic<bool> and having all the other threads periodically check that value to know when to exit.

That approach works well as long as all of your other threads are periodically waking up anyway, at a reasonable frequency.

It's not entirely satisfactory if one or more of your threads is purely event-driven, however -- in an event-driven program, threads are only supposed to wake up when there is some work for them to do, which means that they might well be asleep for days or weeks at a time. If they are forced to wake up every (so many) milliseconds simply to poll an atomic-boolean-flag, that makes an otherwise extremely CPU-efficient program much less CPU-efficient, since now every thread is waking up at short regular intervals, 24/7/365. This can be particularly problematic if you are trying to conserve battery life, as it can prevent the CPU from going into power-saving mode.

An alternative approach that avoids polling would be this one:

  1. On startup, have your main thread create an fd-pipe or socket-pair (by calling pipe() or socketpair())
  2. Have your main thread (or possibly some other responsible thread) include the receiving-socket in its read-ready select() fd_set (or take a similar action for poll() or whatever wait-for-IO function that thread blocks in)
  3. When the signal-handler is executed, have it write a byte (any byte, doesn't matter what) into the sending-socket.
  4. That will cause the main thread's select() call to immediately return, with FD_ISSET(receivingSocket) indicating true because of the received byte
  5. At that point, your main thread knows it is time for the process to exit, so it can start directing all of its child threads to start shutting down (via whatever mechanism is convenient; atomic booleans or pipes or something else)
  6. After telling all the child threads to start shutting down, the main thread should then call join() on each child thread, so that it can be guaranteed that all of the child threads are actually gone before main() returns. (This is necessary because otherwise there is a risk of a race condition -- e.g. the post-main() cleanup code might occasionally free a resource while a still-executing child thread was still using it, leading to a crash)
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 5
    Using [`std::condition_variable<>`](http://en.cppreference.com/w/cpp/thread/condition_variable) is more portable than a socket, and doesn't tie up as many system resources. – AnotherParker Apr 18 '18 at 21:07
  • 1
    @AnotherParker if you have a thread that is blocked in `select()` how do you get that thread to wake up and return from `select()` when a condition variable has been signalled? – Jeremy Friesner Apr 18 '18 at 21:24
  • `select()` isn't portable. Unfortunately the async IO libraries from Boost haven't made it into standard C yet, but eventually... see e.g. https://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/examples/cpp11_examples.html – AnotherParker Apr 18 '18 at 22:05
  • `select()` isn't part of the C++ language standard, but despite that, it's well-supported on every platform I've ever used. (Granted, I've used mainly desktop platforms, I imagine things might be different in low-level-embedded-software-land). Btw if you can describe or demonstrate a solution to this question using the Boost async I/O libraries and a condition variable, you might want to post that as a separate answer; someone might find it helpful. – Jeremy Friesner Apr 18 '18 at 22:59
13

The first thing you must accept is that threading is hard.

A "program using threading" is about as generic as a "program using memory", and your question is similar to "how do I not corrupt memory in a program using memory?"

The way you handle threading problem is to restrict how you use threads and the behavior of the threads.

If your threading system is a bunch of small operations composed into a data flow network, with an implicit guarantee that if an operation is too big it is broken down into smaller operations and/or does checkpoints with the system, then shutting down looks very different than if you have a thread that loads an external DLL that then runs it for somewhere from 1 second to 10 hours to infinite length.

Like most things in C++, solving your problem is going to be about ownership, control and (at a last resort) hacks.

Like data in C++, every thread should be owned. The owner of a thread should have significant control over that thread, and be able to tell it that the application is shutting down. The shut down mechanism should be robust and tested, and ideally connected to other mechanisms (like early-abort of speculative tasks).

The fact you are calling exit(0) is a bad sign. It implies your main thread of execution doesn't have a clean shutdown path. Start there; the interrupt handler should signal the main thread that shutdown should begin, and then your main thread should shut down gracefully. All stack frames should unwind, data should be cleaned up, etc.

Then the same kind of logic that permits that clean and fast shutdown should also be applied to your threaded off code.

Anyone telling you it is as simple as a condition variable/atomic boolean and polling is selling you a bill of goods. That will only work in simple cases if you are lucky, and determining if it works reliably is going to be quite hard.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
3

Additional to Some programmer dude answer and related to discussion in the comment section, you need to make the flag that controls termination of your threads as atomic type.

Consider following case :

bool done = false;
void pending_thread()
{
    while(!done)
    {
        std::this_thread::sleep(std::milliseconds(1));
    }
    // do something that depends on working thread results
}

void worker_thread()
{
    //do something for pending thread
    done = true;
}

Here worker thread can be your main thread also and done is terminating flag of your thread, but pending thread need to do something with given data by working thread, before exiting.

this example has race condition and undefined behaviour along with it, and it's really hard to find what is the actual problem int the real world.

Now the corrected version using std::automic :

std::atomic<bool> done(false);
void pending_thread()
{
    while(!done.load())
    {
        std::this_thread::sleep(std::milliseconds(1));
    }
    // do something that depends on working thread results
}

void worker_thread()
{
    //do something for pending thread
    done = true;
}

You can exit thread without being concern of race condition or UB.

HMD
  • 2,202
  • 6
  • 24
  • 37
  • 2
    ...except this example is using polling, which is a well-known anti-pattern. This will mess up your OS's thread scheduler and cause poor system performance and waste power. – AnotherParker Apr 18 '18 at 20:53
  • 1
    @AnotherParker That's correct, this kind of situation should be handled by `mutex` and `condition_variable`. but the example is just intend to demonstrate the situation that might cause trouble without `automic`. – HMD Apr 19 '18 at 02:35
  • @AnotherParker And the other thing is this approach still may be used in some situations, consider having a render thread that waits for some other thread to produce data frames for it to render, you might want to show some other info or a loading animation while data not supplied, I know you can achieve this using timeout of condition variable but that would have the same effect as this one. It's not always the case of poor system performance or waste power, sometimes your needs are different. – HMD Apr 19 '18 at 03:56