http://jineshkj.wordpress.com/2008/02/02/why-pselect/
All this time I had thought pselect() is simply a luxury, a waste of system call entry, something that i never expected from open source community. I should have blamed POSIX for introducing such a one, but, why would any open source operating system implement it. If you ever wanted to block a set of signal, just calling sigprocmask() before select would be sufficient.
This is where the need for ‘!’ operator helps. ie, right, pselect() is not meant for blocking signals, but rather to unblock them . Such a requirement arises out in event loops, which normally anyone would expect to be implemented as:
while(1)
{
if(need_to_quit)
break;
if(select(…) == -1)
{if(errno == EINTR)
continue;
…
}
…
}
and in the SIGQUIT signal handler you would write :
sigquit_handler()
{
need_to_quit = 1;
}
The problem with such an approach is that if the event comes and get handled after the global volatile(挥发性的) variable ‘need_to_quit’ is checked, but before select() is called, then the event loop would behave as if it has lost the event, and the select() will block till we again get an event.
POSIX recommends the solution to be to use pselect(). Now, how to use it would be what you are thinking, so, here it goes:
step 1 : you block all signals and save the current sigmask
step 2 : check the event condition and do what is required
step 3 : call pselect() and pass it a signal mask to enable all the signals that would provide you the events. when pselect() returns, it will restore the sigmask it had when it was entered(ie, here all the signals masked).
step 4 : you restore the old signal mask
thats it, the race condition is solved. This now makes sure that any signal that comes after checking the variable will be received only after select() has entered. You might now be wondering why we can not do the same thing by writing our own function than a system call. Well here is how it would look :
pselect(..)
{
sigprocmask(.., &new_mask, &old_mask); // enable the signals to be received
select();
sigprocmask(.., &old_mask, null);
}
YES.. If you have noticed, this still gives you a race condition when a signal arrives before select() is called and gets handled immediately after we set new_mask. So, this is the trick with pselect() : Once you call a system call(enter kernel space), you can not be interrupted by anyone else. ie, you can stay in the kernel mode as long as you want; inside the implementation of the system call, we can sleep on blocking functions which can get unblocked on signals if and only if the programmer wishes to do so.
So, the above shown code works when we place it in as a system call; being in kernel mode is itself enough to prevent the race condition . So, if we are using pselect(), this is how our event loop will look like :
sigset_t new_set, old_set;
int ret;
sigfillmask(&new_set);
sigprocmask(.., null, &old_set);
while(1)
{
sigprocmask(.., &new_set, null);
if(need_to_quit)
break;
ret = pselect(.., &old_set) ;
sigprocmask(.., &old_set, null);
if(ret == -1)
{if(errno == EINTR)
continue;
…
}
…
sigprocmask(.., &old_set, null);
}