3

I have a basic sketch of implementing a multithreaded web proxy:

FILE *proxy_log_file;

static void
SIGUSR1_handler(int sig)
{
    (void)sig;
    fflush(proxy_log_file);
}

int
main(int argc, char **argv)
{
    proxy_log_file = fopen("proxy.log", "a");
    Signal(SIGUSR1, SIGUSR1_handler);
}

The idea is that network admins can flush buffered log entries to a log file by using the kill command to send a SIGUSR1 signal to the web proxy. However, I'm not sure it's a good idea to call fflush inside the signal handler. I know fflush is thread-safe but don't think it's async-signal-safe. What concurrency problem could arise from calling fflush inside a signal handler of a multithreaded?

user6269144
  • 318
  • 1
  • 10

1 Answers1

2

Suppose your thread calls a standard IO function which locks a mutex protecting the stream data structures. Before it unlocks that mutex, a signal is delivered, and your signal handler is called. Your signal handler calls fflush() and attempts to lock the mutex. Your thread, and your standard IO stream, will now be deadlocked forever, because your signal handler will wait on the mutex, but it will never become available, because your thread will block until the signal handler returns. It's a classic deadlock.

That's the difference between threads and signal handlers. If a thread attempts to lock a mutex and finds it already locked, it'll just go to sleep, other threads will run, and sooner or later the thread holding the mutex will unlock it. But your signal handler is not a thread, so it won't go to sleep and let the interrupted thread run - that thread will simply block until the signal handler returns which, in the above example, it never will.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • So I need to block the asynchronous delivery and create a dedicated thread that receives the signal synchronously by, say, calling `sigwait`? – user6269144 Apr 30 '16 at 19:27
  • 1
    No, you need to just not call functions that are not async-safe from your signal handlers. It's not safe to do that. Your signal handler can set a flag that you've declared as `volatile sig_atomic_t`, for instance, and then return, and one of your threads can flush the stream if it detects that flag has been set. Or, alternatively, use a mechanism other than signals, for this. – Crowman Apr 30 '16 at 19:33
  • What you wrote applies to mutex-protected streams only. The latter are standardised since C11. Prior to the C11 Standard the code probably would not deadlock, but still could exhibit some other kind of strange behaviour, like for example flushing a half-filled buffer of another thread. – alk Apr 30 '16 at 19:45
  • @alk: True, although POSIX has required standard IO functions to be threadsafe for as long as there have been POSIX threads, so on the kind of UNIX-like platform that the question deals with, the streams have probably been mutex-protected for longer than that. – Crowman Apr 30 '16 at 19:52