Repo: ovfor4/ccshell/module-test/self_pipe.c
Problem
Consider
if (not_signald())
read(STDIN_FILENO, s, len);
No matter SA_RESTART is set or not, if signal’d between check and read, there will be race
Conclusion: signal delivering is an instant event, but we want it to be durable event, and need to find some calls to return immediately if such events happen; and if we check such flags manually, atomicity is bad, so race can happen if it’s signal’d between these statements
select(2) or poll(2) can check multiple file descriptors at the same time, so if we write something to the pipe, it’s durable, so we can wait for either stdin or our pipe of signal handler
Limits of select(2)
POSIX standard says, without specifying the size
The <sys/select.h> header shall define the fd_set type as a structure.
On many implementations, the size is very limited for modern applications
- on Linux
select() can monitor only file descriptors numbers that are less than FD_SETSIZE (1024) […] and this limitation will not change"
- on FreeBSD
The default size of FD_SETSIZE is currently 1024.
“Luckily”, we can define FD_SETSIZE or use a hack (malloc manually and just get a pointer), but it’s not portable, and it’s dependent on the platform
Notes for poll(2)
select(2) is an exception of SA_RESTART
If SA_RESTART has been set for the interrupting signal, it is implementation-defined whether the function restarts or returns with [EINTR].
poll(2) shall NOT be an exception, that is, if signal’d, it shall be restarted per standard
If set, and a function specified as interruptible is interrupted by this signal, the function shall restart and shall not fail with [EINTR] unless otherwise specified
However, Linux, FreeBSD, Darwin are bad kernels
- on Linux,
signal(7)
The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler: […] poll(2)"
- on FreeBSD, documentation doesn’t specify the behavior of
poll(2), but we can find the answer in source code
// freebsd/sys/kern/sys_generic.c
// kern_poll_kfds()
/* poll is not restarted after signals... */
if (error == ERESTART)
error = EINTR;
if (error == EWOULDBLOCK)
error = 0;
- on Darwin, like FreeBSD, it doesn’t specify the behavior too, but we can still find similar code
// darwin/bsd/kern/sys_generic.c
// poll_nocancel()
/* poll is not restarted after signals... */
if (error == ERESTART) {
error = EINTR;
}
if (error == 0) {
error = copyout(fds, uap->fds, nfds * sizeof(struct pollfd));
*retval = rfds;
}
But we can simply wrap it, so it can auto restart no matter SA_RESTART is set or not, nor the implementation restarts it or not
If it would be auto restarted, poll should not return as if signal’d
Otherrise, when signal’d, poll should return as if signal’d first, and then we restart to check things in pipe, and this should return immediately (our signal handlers should put something into pipe)
int my_poll(struct pollfd fds[], nfds_t nfds)
{
while (true)
{
int ret = poll(fds, nfds, -1);
// if not signal'd
if (!(ret == -1 && errno == EINTR))
return ret;
}
}