Discussion:
sigtimedwait() implementation
(too old to reply)
William Ahern
2011-12-09 21:41:32 UTC
Permalink
Anyone spot any noteworthy issues in this implementation, other than
siginfo_t not being filled out completely and the use NSIG?

It seemed more convenient to write this than refactor the code relying on
the interface. I've tested this on OS X and OpenBSD and it seems to work.

I suppose it also doesn't handle EINTR correctly, but I can live with that.


int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) {
struct timespec elapsed = { 0, 0 }, rem;
sigset_t pending;
int signo;
long ns;

do {
sigpending(&pending); /* doesn't clear pending queue */

for (signo = 1; signo < NSIG; signo++) {
if (sigismember(set, signo) && sigismember(&pending, signo)) {
if (info) {
memset(info, 0, sizeof *info);
info->si_signo = signo;
}

return signo;
}
}

ns = 200000000L; /* 2/10th second */
nanosleep(&(struct timespec){ 0, ns }, &rem);
ns -= rem.tv_nsec;
elapsed.tv_sec += (elapsed.tv_nsec + ns) / 1000000000L;
elapsed.tv_nsec = (elapsed.tv_nsec + ns) % 1000000000L;
} while (elapsed.tv_sec < timeout->tv_sec || (elapsed.tv_sec == timeout->tv_sec && elapsed.tv_nsec < timeout->tv_nsec));

errno = EAGAIN;

return -1;
} /* sigtimedwait() */
Rainer Weikusat
2011-12-09 22:37:26 UTC
Permalink
Post by William Ahern
Anyone spot any noteworthy issues in this implementation, other than
siginfo_t not being filled out completely and the use NSIG?
[...]
Post by William Ahern
int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) {
struct timespec elapsed = { 0, 0 }, rem;
sigset_t pending;
int signo;
long ns;
do {
sigpending(&pending); /* doesn't clear pending queue */
for (signo = 1; signo < NSIG; signo++) {
if (sigismember(set, signo) && sigismember(&pending, signo)) {
if (info) {
memset(info, 0, sizeof *info);
info->si_signo = signo;
}
return signo;
}
}
ns = 200000000L; /* 2/10th second */
nanosleep(&(struct timespec){ 0, ns }, &rem);
ns -= rem.tv_nsec;
elapsed.tv_sec += (elapsed.tv_nsec + ns) / 1000000000L;
elapsed.tv_nsec = (elapsed.tv_nsec + ns) % 1000000000L;
} while (elapsed.tv_sec < timeout->tv_sec || (elapsed.tv_sec == timeout->tv_sec && elapsed.tv_nsec < timeout->tv_nsec));
errno = EAGAIN;
return -1;
} /* sigtimedwait() */
It doesn't make the pending signal go away. This could be accomplished
by changing the corresponding the signal disposition to SIG_IGN and
back to its original value before returning.

IMO, a better implementation idea would be to install handlers for all
signals in set. These handlers would siglongjmp to a target set up
while the signals in set are still blocked, restoring the original
signal mask saved by sigsetjmp and using the number of the caught
signal as second argument to the jump call. After the jump target has
been established, the code would unblock the signals in set and then
sleep for the specified amount of time, possibly in a loop in order to
deal with EINTRs caused by other signal handlers catching
signals. Provided no signal was caught, it should then restore the
original signal mask and the original set of signal handlers and
return -1, setting errnor to EAGAIN. If a signal was caught, only
restoring the original handlers and returning the non-zero return
value of sigsetjmp would be needed.
William Ahern
2011-12-10 00:09:58 UTC
Permalink
Post by Rainer Weikusat
Post by William Ahern
Anyone spot any noteworthy issues in this implementation, other than
siginfo_t not being filled out completely and the use NSIG?
<snip>
Post by Rainer Weikusat
It doesn't make the pending signal go away.
d'oh!
Post by Rainer Weikusat
This could be accomplished by changing the corresponding the signal
disposition to SIG_IGN and back to its original value before returning.
The original idea was to call sigwait() once sure (in the absence of
threads) that it wouldn't block. It completely slipped my mind.
Post by Rainer Weikusat
IMO, a better implementation idea would be to install handlers for all
signals in set. These handlers would siglongjmp to a target set up
while the signals in set are still blocked, restoring the original
signal mask saved by sigsetjmp and using the number of the caught
signal as second argument to the jump call. After the jump target has
been established, the code would unblock the signals in set and then
sleep for the specified amount of time, possibly in a loop in order to
deal with EINTRs caused by other signal handlers catching
signals. Provided no signal was caught, it should then restore the
original signal mask and the original set of signal handlers and
return -1, setting errnor to EAGAIN. If a signal was caught, only
restoring the original handlers and returning the non-zero return
value of sigsetjmp would be needed.
Pretty clever. I might code this up and see how it compares complexity-wise
with another implementation I just wrote using kqueue/kevent to detect sent
signals.

Loading...