Mark [1] has a bit more to say about signal handlers [2]:
From: Mark Grosberg <XXXXXXXXXXXXXXXXX>
To: sean@conman.org
Subject: Signal handlers.
Date: Mon, 22 Oct 2007 14:36:05 -0400 (EDT)
> You know, now that I think about it even the write() system call is not totally safe in signal handlers. Why? Because lets say the write system call fails. Furthermore lets say that the signal was delivered somewhere in the return path of some other function, like, oh, I dunno, a socket I/O (Input/Output) call.
So the socket I/O call has been performed and we are in the midst of unwinding through the various layers of libc and have set errno to some important value (like say HOST-NOT-REACHABLE) and then our signal handler attempts to write … poof our important errno value gets corrupted!
…
Amusingly from the man page for signal on my system:
> > Additionally, inside the signal handler it is also considered more safe to make a copy of the global variable errno and restore it before returning from the signal handler.
>
Too bad if you are in a threaded program “errno” is actually #defined as something like (*__pthreads_get_errno_location()). Hahah! You're so double-XXXXXX with this its not funny. Assuming that getting the errno location is atomic, is loading/storing errno? What if you have some goofy architecture where writes to errno are not atomic and require multiple instructions. That's why sig_atomic_t is so special … it's guaranteed atomic.
The fact that errno is a global variable is a consequence of the C language and it's rather poor support for multiple return values (it can—it's just that you have to pass in additional pointers). At the time, it was the easiest thing that could possibly work [3] (I swear—at times, it feels like if it wasn't in Unix V7, then it doesn't really work that well under modern versions of Unix, like threads), not the best thing that could possibly work (like writing a systems language that supported multiple return values).
And the whole (*__pthreads_get_errno_location()) is telling—the C Standard requires that errno “expands to a modifiable lvalue”—in other words, it could be a pointer to a location which can be changed.
In fact, the C Standard on errno only covers half a page, and P. J. Plauger [4] states in his book The Standard C Library: [5]
If I had to identify one part of the C Standard that is uniformly disliked, I would not have to look far. Nobody likes errno or the machinery that it implies. I can't recall anybody defending this approach to error reporting, not in two dozen or more meetings of X3J11 [The ANSI (American National Standards Institute) working committee on standardizing C in the late 80s. —Editor], the committee that developed the C Standard. Several alternatives were proposed over the years. At least one faction favored simply discarding errno. Yet it endures.
The C Standard has even added to the existing machinery. The header <errno.h> is an invention of the committee. We wanted to have every function and data object in the library declared in some standard header. We gave errno its own standard header mostly to ghettoize it. We even added some words in the hope of clarifying a notoriously murky corner of the C language.
A continuing topic among groups working to extend and improve C is how to tame errno. Or how to get rid of it. The fact that no clear answer has emerged to date should tell you something. There are no easy answers when it comes to reporting and handling errors.
It's a rather depressing chapter to read, and P. J. Plauger is right—reporting and handling of errors is difficult reguardless of language [6].
But errno isn't the only problematic area with signals, so too might be pthreads [7], making a three-for-one clusterXXXX of your code.
Sigh.
[3] http://c2.com/cgi/wiki?DoTheEasiestThingThatCouldPossiblyWork
[5] http://www.amazon.com/exec/obidos/ASIN/0131315099/conmanlaborat-20
[7] http://www.kaourantin.net/2006/08/pthreads-and-signals.html