3

I searched a lot but none answered my question, I read that it's not safe to use cout in signal handlers like this:

void ctrlZHandler(int sig_num) {
    //SIGTSTP-18
    std::cout << "smash: got ctrl-Z" << std::endl;
    SmallShell::route_signal(sig_num);
}
  1. will it solve the problem if I move the printing inside route_signal?

  2. Is there a lists of safe-to-call functions in C++11?

  3. What if the only solution to use write, can you show me short example, and let's say route_signal have 100 printings should I replace all with write()? that sounds exhausting with the need to allocate memory and free...

  • it is unsafe because the buffer while writing to `std::cout`. but you can open `std::cout` to write without buffer ant then it could be safer. – Ido Apr 27 '21 at 11:38
  • 2
    [signal safety](https://man7.org/linux/man-pages/man7/signal-safety.7.html) means no memory allocation. And no buffered I/O, since it won't block signaling (unless you've taken measures to ensure signals are blocked for those routines). All routines called by the signal handler have the same constraints. – Eljay Apr 27 '21 at 11:41
  • Please provide some [mre] in your question (some C++ code with a `main`). **At the very least, tell a lot more about your application**. Does it has some event loop? Is it multi-threaded? Do you have access to *all* the source code? Is some C++ code generated by a software (which one)? Please [edit](https://stackoverflow.com/posts/67282215/edit) your question to add several paragraphs in it. Read also [*Advanced Linux Programming*](https://mentorembedded.github.io/advancedlinuxprogramming/) and [syscalls(2)](https://man7.org/linux/man-pages/man2/syscalls.2.html). Spend several hours in reading – Basile Starynkevitch Apr 27 '21 at 11:57
  • **Examples of C++ code handling signals** is the [ninja](https://ninja-build.org/) builder or the [fish](https://fishshell.com/) shell (and soon the [RefPerSys](http://refpersys.org/) project) **Both are open source, you are allowed to download and study their source code**. – Basile Starynkevitch Apr 27 '21 at 12:00
  • Read also [signal(7)](https://man7.org/linux/man-pages/man7/signal.7.html) and a good [operating system textbook](https://pages.cs.wisc.edu/~remzi/OSTEP/) – Basile Starynkevitch Apr 27 '21 at 12:03
  • Since the context appears to be a `shell` of sorts I'm guessing the main loop is based on something like `select`, `poll` or similar. If that's the case then you might want to look at the [`self-pipe` trick](https://man7.org/linux/man-pages/man2/select.2.html#NOTES). – G.M. Apr 27 '21 at 12:08

3 Answers3

5

The reason why using std::cout inside signal handlers isn't recommented is because signals might interrupt your running code whenever and std::cout::operator << is not reentrant.

This means if you are executing std::cout::operator << when a signal is raised that also uses it within it's execution, the result is undefined.

So, no. Moving it into route_signal would not solve this and you should replace every call of std::cout within!

One workaround would be to set a flag that this signal was received and create a output outside the signal handler after it returned.

Detonar
  • 1,409
  • 6
  • 18
  • This doesn't answer my question in anyway, I didn't ask why it's not safe –  Apr 27 '21 at 11:38
4

Signal handlers need to run quickly and be reentrant, which is why they shouldn’t call output stream functions like cout <<, either directly or indirectly.

If you are doing this temporarily under controlled conditions for testing, it might be okay, but make sure the signal you are handling is not triggered again until the handler has finished and be aware that stream functions can be slow, which might mess up your tests as well.

James McLeod
  • 2,381
  • 1
  • 17
  • 19
1
  1. will it solve the problem if I move the printing inside route_signal?

No.

  1. Is there a lists of safe-to-call functions in C++11?

For practical purposes, the only safe thing you can do is set a volatile sig_atomic_t or lock-free atomic flag inside a signal handler. (N3690 intro.execution §1.9 ¶6)

I'm no C nor C++ language lawyer, but I believe anything permitted in a conforming C application is allowed in a C++11 signal handler. However, that set is very, very limited: abort, quick_exit, _Exit, and signal. (ISO/IEC 9899:2011 §7.14.1.1 ¶5).

  1. What if the only solution to use write, can you show me short example, and let's say route_signal have 100 printings should I replace all with write()? that sounds exhausting with the need to allocate memory and free...

A better solution is to redesign your program to use sigwait or to check that a flag safely set inside the signal handler.

If you insist on using write, and if you trust that it is safe to call inside a signal handler in your C++ implementation — which it probably is but, again, is not guaranteed by C++ itself — then you simply have a coding problem. You'll need to figure out formatting yourself, bearing in mind that even on POSIX-conforming systems malloc and free are not async-signal-safe. It can certainly be done.

pilcrow
  • 56,591
  • 13
  • 94
  • 135