How to Make a Function Async-Signal-Safe

How to make a function async-signal-safe?

You can use fcntl() as an alternative to flock().

Signal handler async safe functions

Technically you can call or write any function(s) inside the signal handler there is nothing preventing you from doing it.

The reason why its discouraged is that, when a signal handler is handling your operation, there could be another signal raised which could make the signal handler jump to the higher priority signal handler.

This kind of leads to race and hard to debug problems as we are not aware of the ordering of the handling of signals.

Thats the reason why the signal handlers are supposed to be light to avoid hard to debug race conditions and usually we set flags to indicate a signal has been raised and handle it in the main or another thread that reads these flags.

Do functions which are not defined as async-safe, such as mmap(2), effect other async-safe functions called in signal handler?

Yes.

If your substitute open() calls a function that is not async-signal-safe, then it is not safe for a signal handler to call your function. That it has the same name and signature as a standard function that is async-signal-safe is irrelevant. That it calls the replaced function or other async-signal-safe functions is irrelevant. That the prospective signal handler's call to a function that is not async-signal-safe would not be a direct one is irrelevant.

In response to the question update: in the event that the function presented in the question is called as a signal handler, it has undefined behavior on account of its call to mmap(). The details of that UB cannot be predicted, at least not from the relevant standards. That's what "undefined" means. There is no reason whatever to suppose that the actual and apparent effects of the open() call would be somehow protected from interference. Nor the general signal-handling mechanism. Nor anything else in the program.

The further you get from the locus of the UB, the less likely is any noticeable effect, and the more likely the OS is to contain it, but UB is not something to mess with. In principle, it may manifest as any behavior or behaviors within the power of your computer to produce, such as wiping your disk, turning off your CPU fan, or mailing your password to hackers.

Why can only async-signal-safe functions be called from signal handlers safely?


Say a thread within process A is calling printf and another thread
receives the signal and then calls printf. Is it possibly because the
kernel here will not know what to do because it will not be able to
distinguish between the two calls.

It's not the kernel that will have issues. It's your application itself. printf is not a kernel function. It's a function in the C library, that your application uses. printf is actually a fairly complicated function. It supports a wide variety of output formatting.

The end result of this formatting is a formatted output string that's written to standard output. That process in and of itself also involves some work. The formatted output string gets written into the internal stdout file handle's output buffer. The output buffer gets flushed (and only at this point the kernel takes over and writes a defined chunk of data to a file) whenever certain defined conditions occur, namely when the output buffer is full, and/or whenever a newline character gets written to the output stream.

All of that is supported by the output buffer's internal data structures, which you don't have to worry about because it's the C library's job. Now, a signal can arrive at any point while printf does its work. And I mean, at any time. It might very well arrive while printf is in the middle of updating the output buffer's internal data structure, and they're in a temporarily inconsistent state because printf hasn't yet finished updating it.

Example: on modern C/C++ implementations, printf may not be signal-safe, but it is thread safe. Multiple threads can use printf to write to standard output. It's the threads' responsibility to coordinate this process amongst themselves, to make sure that the eventual output actually makes sense, and it's not jumbled up, at random, from multiple threads' output, but that's beside the point.

The point is that printf is thread safe, and that typically means that somewhere there's a mutex involved in the process. So, the sequence of events that might occur is:

  • printf acquires the internal mutex.

  • printf proceeds with its work with formatting the string and writing it to stdout's output buffer.

  • before printf is done, and can release the acquired mutex, a signal arrives.

Now, the internal mutex is locked. The thing about signal handlers is that it's generally not specified which thread, in a process, gets to handle the signal. A given implementation might pick a thread at random, or it might always pick the thread that's currently running. In any case, it can certainly pick the thread that has locked the printf, here, in order to handle the signal.

So now, your signal handler runs, and it also decides to call printf. Because printf's internal mutex is locked, the thread has to wait for the mutex to get unlocked.

And wait.

And wait.

Because, if you were keeping track of things: the mutex is locked by the thread that was interrupted to service the signal. The mutex won't get unlocked until the thread resumes running. But that won't happen until the signal handler terminates, and the thread resumes running, but the signal handler is now waiting for the mutex to get unlocked.

You're boned.

Now, of course, printf might use the C++ equivalent of std::recursive_mutex, to avoid this problem, but even this won't solve all possible deadlocks that could get introduced by a signal.

To summarize, the reason why it's "unsafe to receive a signal and call a non async safe function from within that signal handler" is because it's not, by definition. It's not safe to call a non-async safe function from within the signal handler" because the signal is an asynchronous event, and since it's not an async-safe function, you can't, by definition. Water is wet because it's water, and an async-unsafe function cannot be called from an asynchronous signal handler.

Async safe writing functions for Signal handler on windows

There is little authoritative information on this in general, even on POSIX. Instead, I'd suggest posting to a queue/signaling a condition from inside the async handler (using safe primitives) and doing the rest elsewhere.

TTBOMK this abstraction is already present and ready-to-use in Boost:

  • Boost Asio signal_set

Also, for your particular purpose it does appear that Boost Stacktrace has helper functions that are expressly async-safe: Handling terminates, aborts and Segmentation Faults:

#include <signal.h>     // ::signal, ::raise
#include <boost/stacktrace.hpp>

void my_signal_handler(int signum) {
::signal(signum, SIG_DFL);
boost::stacktrace::safe_dump_to("./backtrace.dump");
::raise(SIGABRT);
}

Is sysinfo() async. signal safe?

sysinfo is just thin system call wrapper and async-signal-safe:

00000000000feb00 <sysinfo@@GLIBC_2.2.5>:
feb00: mov $0x63,%eax
feb05: syscall
feb07: cmp $0xfffffffffffff001,%rax
feb0d: jae feb10 <sysinfo@@GLIBC_2.2.5+0x10>
feb0f: retq
feb10: mov 0xbf359(%rip),%rcx
feb17: neg %eax
feb19: mov %eax,%fs:(%rcx)
feb1c: or $0xffffffffffffffff,%rax
feb20: retq

But there is no vDSO acceleration for it, so it is going to be much slower than clock_gettime with either CLOCK_REALTIME or CLOCK_MONOTONIC on current systems.

Print int from signal handler using write or async-safe functions

If you really insist on doing the printing from a signal handler, you basically have 2 options:

  1. Block the signal except in a dedicated thread you create for handling the signal. This special thread can simply perform for (;;) pause(); and since pause is async-signal-safe, the signal handler is allowed to use any functions it wants; it's not restricted to only async-signal-safe functions. On the other hand, it does have to access shared resources in a thread-safe way, since you're now dealing with threads.

  2. Write your own code for converting integers to decimal strings. It's just a simple loop of using %10 and /10 to peel off the last digit and storing them to a short array.

However, I would highly recommend getting this operation out of the signal handler, using the self-pipe trick or similar.

C write async unsafe code in signal handler

In a signal handler you can only use a set of safe functions which in many cases is sufficient for complicated functionality started within a handler. You can check man pages for your system for 'signal-safety' or similar. Here is a pointer on the web: https://man7.org/linux/man-pages/man7/signal-safety.7.html

pthread synchronization functions are not on the list.

However, One of the function listed there is sem_post: https://man7.org/linux/man-pages/man3/sem_post.3.html

sem_post() is async-signal-safe: it may be safely called within a
signal handler.

So, you can implement mutex-like synchronization using semaphores within the signal handler.



Related Topics



Leave a reply



Submit