I would suggest starting a comparison with select() vs poll() . Linux also provides both pselect() and ppoll() ; and the additional argument const sigset_t * for pselect() and ppoll() (vs select() and poll() ) has the same effect on each "p-variant". If you do not use signals, you do not have races for protection, so the basic question is really about the effectiveness and ease of programming.
Meanwhile, stackoverflow.com already has an answer here: what is the difference between a poll and a choice .
As for the race: as soon as you start using signals (for some reason), you will find out that in the general case, the signal handler should just set a variable like volatile sig_atomic_t to indicate that the signal has been detected. The main reason for this is that many library calls are not re-entrant , and the signal can be delivered while you are “in the middle” of such a procedure. For example, simply printing the message into a stream-style data structure such as stdout (C) or cout (C ++) can lead to reconnection issues.
Suppose you have code that uses the volatile sig_atomic_t flag variable, perhaps to catch a SIGINT , something like this (see also http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html ) :
volatile sig_atomic_t got_interrupted = 0; void caught_signal(int unused) { got_interrupted = 1; } ... struct sigaction sa; sa.sa_handler = caught_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sa, NULL) == -1) ... handle error ... ...
Now, in the main part of your code, you can "run until interrupted":
while (!got_interrupted) { ... do some work ... }
This is normal until you start making calls waiting for I / O, such as select or poll . The wait action must wait for this I / O, but it also needs to wait for the SIGINT interrupt. If you just write:
while (!got_interrupted) { ... do some work ... result = select(...); }
then it is possible that the interrupt will happen just before you call select() or poll() , and not later. In this case, you got an interrupt - and the got_interrupted variable got_interrupted set, but after that you start to wait. You should have checked the got_interrupted variable before you started to wait, not after.
You can try to write:
while (!got_interrupted) { ... do some work ... if (!got_interrupted) result = select(...); }
This shortens the "race window" because now you will find an interrupt if this happens when you are in the "do some work" code; but there is still a race, because interruption can occur immediately after checking the variable, but right before the choice or poll.
The solution is to make the sequence "test, then wait" "atomic" using the signal blocking properties of sigprocmask (or in the POSIX stream code, pthread_sigmask ):
sigset_t mask, omask; ... while (!got_interrupted) { ... do some work ... sigemptyset(&mask); sigaddset(&mask, SIGINT); if (sigprocmask(SIG_BLOCK, &mask, &omask)) ... handle error ... if (got_interrupted) { sigprocmask(SIG_SETMASK, &omask, NULL); break; } result = pselect(..., &omask); sigprocmask(SIG_SETMASK, &omask, NULL); }
(the above code is actually not so large, it is structured to illustrate rather than efficiency - it’s more efficient to manipulate the signal masses a little differently, and differently place the tests with "interrupted").
Until you actually start catching SIGINT , you only need to compare select() and poll() (and if you start to need a lot of descriptors, some of the events like epoll() are more efficient than one).