Monday, October 22, 2007
Friends don't let friends write signal handlers
Mark has a bit more to say about signal handlers:
- 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 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 whysig_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 (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
states in his book The
Standard C Library:
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 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 discardingerrno
. 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 gaveerrno
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.
But errno
isn't the only problematic area with signals, so
too might be pthreads,
making a three-for-one clusterXXXX of your
code.
Sigh.