From e4348f29c1e0bca44ea43b35cb18ca9b58dbe16b Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Mon, 8 Dec 2014 23:26:20 +0000 Subject: Documentation update: safe wrappers --- doc/djblegacy.html | 2 +- doc/libstddjb/safewrappers.html | 66 ++++++++++++++++++++++------------------- doc/libstddjb/selfpipe.html | 23 +++++++------- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/doc/djblegacy.html b/doc/djblegacy.html index 0cdd891..716223f 100644 --- a/doc/djblegacy.html +++ b/doc/djblegacy.html @@ -121,7 +121,7 @@ and network programmer's point of view, one thing is clear: most standard libc interfaces suck. There is no buffered asynchronous I/O. There is no timed I/O. There is no heap management helper. Even simple system calls are not -guaranteed to succeed! +always guaranteed to succeed!

diff --git a/doc/libstddjb/safewrappers.html b/doc/libstddjb/safewrappers.html index 6d889d6..37d2425 100644 --- a/doc/libstddjb/safewrappers.html +++ b/doc/libstddjb/safewrappers.html @@ -40,51 +40,55 @@ handler, if any, has been executed).

- This means that the intended execution of the process is at the mercy -of a stray signal. If a signal happens at the wrong time, a system call -fails when it could have succeeded. This is not acceptable. + Most of the time, this is not an issue: unignored signals usually kill +the process anyway. Or stop it - and when it resumes, system calls are +simply restarted, not interrupted. EINTR can only happen when a signal +handler has been installed, and a signal is actually caught by the +signal handler during an interruptible system call. And to avoid EINTR, +users can use the SA_RESTART flag when installing the signal handler with +sigaction().

-

The solution

+

+ However, there is a common case where using SA_RESTART is a bad idea: +when writing an asynchronous event loop. +

- So, in order to be perfectly reliable, when a program makes an interruptible -system call, it must check whether the return value is -1 EINTR, -and restart the system call if it is the case. This is annoying to write; -so, libstddjb provides small wrappers around interruptible system -calls, so that programmers can just call those safe wrappers and -never bother with this again. + An asynchronous event loop is organized around a +select() or +poll() +system call that is the only blocking operation in the whole loop. That call takes a +timeout as an argument. If a signal handler is installed with SA_RESTART and a +signal arrives in the middle of the select/poll call - which happens often +since it is blocking - then the select/poll is restarted with the same arguments, +including the timeout. This is not good: time has elapsed, the timeout should be +recomputed. Some versions of select update the values in the struct timeval even +when select() is interrupted, but it is not portable, and not applicable to poll(). +So it is necessary, in this case, to have select/poll return -1 EINTR when a +signal is caught. And that means SA_RESTART should not be used.

- The performance loss from having a wrapper layer is totally negligible -compared to the cost of using a system call in the first place. + Which means that other system calls performed when the signal handler has +been installed, for instance in the body of the loop, will not be +protected, and can return -1 EINTR if a signal is caught at the wrong time.

-

But isn't it what the SA_RESTART flag is meant to address?

+

The solution

- Yes, it is. Unfortunately, SA_RESTART only protects interruptible -system calls from signals you actually have control over, and set a -handler for with -sigaction(). -This is not enough. You cannot decide that every signal sent -to your process should have SA_RESTART behaviour; and the Single Unix -specification says nothing about signals you do not control. For instance, -you cannot trap SIGSTOP; SIGSTOP does not kill your process, which -should resume flawlessly at the next SIGCONT; and according to the -specification, it is valid for SIGSTOP and SIGCONT to not -have SA_RESTART behaviour. So if you get a SIGSTOP while performing -an interruptible system call, that system call may return -1 EINTR, -this is not an OS bug, and there's nothing you can do about it with -sigaction(). + So, in order to be perfectly reliable, when a program has caught a signal +without SA_RESTART and makes an interruptible system call, it must check whether +the return value is -1 EINTR, and restart the system call if it is the case. +This is annoying to write; so, skalibs provides small wrappers around +interruptible system calls, so that programmers can just call those safe wrappers +and never bother with this again.

- SA_RESTART is only a partial solution: in other words, it doesn't work. -Until the Single Unix specification explicitly states that untrapped -non-lethal signals MUST have SA_RESTART behaviour by default, you -need safe wrappers to protect interruptible system calls. + The performance loss from having a wrapper layer is totally negligible +compared to the cost of using a system call in the first place.

diff --git a/doc/libstddjb/selfpipe.html b/doc/libstddjb/selfpipe.html index 7eff430..d57a586 100644 --- a/doc/libstddjb/selfpipe.html +++ b/doc/libstddjb/selfpipe.html @@ -39,12 +39,9 @@ screw up, and write buggy code.

- Unix is tricky enough with interruptions. Most of libstddjb's wrappers -are there to protect system calls from EINTR. (And no, the SA_RESTART -option in sigaction() isn't protection -enough.) But signal handlers are -more than just pesky interruptions: they can totally change the -execution flow. They mess up the logic of linear and structured code, + Unix is tricky enough with interruptions. Even when you have a single +thread, signals can make the execution flow very non-intuitive. +They mess up the logic of linear and structured code, they introduce non-determinism; you always have to think "and what if I get interrupted here and the flow goes into a handler...". This is annoying. @@ -109,9 +106,13 @@ environment (the "bottom half" of the handler).

The selfpipe library does it all for you - you don't even have to write the top half yourself. You can forget their existence and recover -some peace of mind. Of course, you still need to protect your -system calls against EINTR: the self-pipe trick doesn't prevent signals -from happening. +some peace of mind. +

+ +

+ Note that in an asynchronous event loop, you need to protect your +system calls against EINTR by using safe +wrappers.

How do I use it ?

@@ -167,14 +168,14 @@ r = selfpipe_trapset(&set) ;

selfpipe_trap() and selfpipe_untrap() handle signals one by one. Alternatively (and often preferrably), you can use -selfpipe_trapset() to directly handle signal sets. When you call +selfpipe_trapset() to directly handle signal sets. When you call selfpipe_trapset(), signals that are present in set will be caught by the selfpipe, and signals that are absent from set will be uncaught. r is 0 if the operation succeeded and -1 if it failed.

-

Handling events 

+

Handling events

 int c = selfpipe_read() ;
-- 
cgit v1.2.3