I am currently studying the concepts of network programming in which I came across one of the functions pselect() which resolves the issue of select ie. with select(), there is a chance of problem which is that between the test of intr_flag and the call to select, if the signal occurs, it will be lost if select blocks forever.
if (intr_flag)
handle_intr(); /* handle the signal */
if ( (nready = select( ... )) < 0) {
if (errno == EINTR) {
if (intr_flag)
handle_intr();
}
However, it says that With pselect, we can now code this example reliably as
sigset_t newmask, oldmask, zeromask;
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */
if (intr_flag) //here
handle_intr(); /* handle the signal */
if ( (nready = pselect ( ... , &zeromask)) < 0) {
if (errno == EINTR) { //here
if (intr_flag)
handle_intr ();
}
...
}
The explanation that it gives for the code to be reliable is that - Before testing the intr_flag variable, we block SIGINT. When pselect is called, it replaces the signal mask of the process with an empty set (i.e., zeromask) and then checks the descriptors, possibly going to sleep. But when pselect returns, the signal mask of the process is reset to its value before pselect was called (i.e., SIGINT is blocked).
But in the code with pselect mentioned above, we block the signals then how can we check the error EINTR? Since the pselect blocks all the signals then when Interrupt occurs it should block that from disrupting or being delivered to the process. It is only when pselect returns then the signal can be delivered.
According to the lines in front of which comment here is mentioned, INTERRUPT signal can still happen before pselect is called or in-between the first check and pselect or when pselect is called contradicting the purpose of blocking the Interrupt and any other signals and thus should lead to race condition as with select is there.
Please anyone explain how this is possible as I am new to these concepts.
Well, the main idea is that doing
ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
is equivalent to performing the following actions atomically:To make use of this we first block, say,
SIGINT
:At that point we can't receive
SIGINT
and, therefore, we can't process it. But we don't need it until we enterpselect()
. What we want to do after we've blockedSIGINT
is to setup a proper signal handler.Say, we have a flag
static volatile int intr_flag = 0;
declared outside our main code and we define a handler calledhandler()
which simply doesintr_flag = 1;
. So, we set it up as a handler:Then we configure
readfs
(declaration not show here), initialise an empty signal set and callpselect()
:So, I'll outline this one more time - we don't receive
SIGINT
until we callpselect()
. We don't need it. As soon as we enterpselect()
, signalSIGINT
will be unblocked (because of an empty signal set provided topselect()
), andpselect()
can be interrupted bySIGINT
. At the point whenpselect()
returns, we again will not receive any furtherSIGINT
, but ifSIGINT
has occurred duringpselect()
, then we will detect this as pererrno
beingEINTR
and if we checkintr_flag
, we'll find it to be1
. We will understand that we need to behave accordingly. So, it's pretty clear that signal handler may do its job as soon as the signal is unblocked, and the latter happens withinpselect()
call itself.The most important detail here is that if we don't have a special
pselect()
call implemented in atomic way, then we have to do the steps around/* NB: NOTE-1 */
label in the snippet above ourselves when using a usualselect()
. And, as it will not be atomic, we will have a chance thatSIGINT
will be delivered to us in-between the two actions - where/* NB: NOTE-1 */
hint is located, i.e. after we unblock the signal from being delivered and beforeselect()
is entered. At that point the signal indeed will be lost. Here is why we need apselect()
call instead.All in all, atomicity of
pselect()
is the explanation for its use. If you are not quite familiar with such a concept, you may refer to the article on Wikipedia or to special books on computer science topics.Also, I'll furnish my answer with the link to an article on LWN which is far more exhaustive.