From e4348f29c1e0bca44ea43b35cb18ca9b58dbe16b Mon Sep 17 00:00:00 2001
From: Laurent Bercot
- 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().
-+ 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.
-- 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.