diff options
Diffstat (limited to 'libc/unistd/sleep.c')
-rw-r--r-- | libc/unistd/sleep.c | 125 |
1 files changed, 80 insertions, 45 deletions
diff --git a/libc/unistd/sleep.c b/libc/unistd/sleep.c index b0031f022..9db115a29 100644 --- a/libc/unistd/sleep.c +++ b/libc/unistd/sleep.c @@ -24,11 +24,54 @@ #include <unistd.h> - /* version perusing nanosleep */ #if defined __UCLIBC_HAS_REALTIME__ -#if 0 +/* I am unable to reproduce alleged "Linux quirk". + * I used the following test program: +#include <unistd.h> +#include <time.h> +#include <signal.h> +static void dummy(int sig) {} +int main() { + struct timespec t = { 2, 0 }; + if (fork() == 0) { + sleep(1); + return 0; + } + signal(SIGCHLD, SIG_DFL); // + signal(SIGCHLD, dummy); // Pick one + signal(SIGCHLD, SIG_IGN); // + nanosleep(&t, &t); + return 0; +} + * Testing on 2.4.20 and on 2.6.35-rc4: + * With SIG_DFL, nanosleep is not interrupted by SIGCHLD. Ok. + * With dummy handler, nanosleep is interrupted by SIGCHLD. Ok. + * With SIG_IGN, nanosleep is NOT interrupted by SIGCHLD. + * It looks like sleep's workaround for SIG_IGN is no longer needed? + * The only emails I can find are from 1998 (!): + * ---------- + * Subject: Re: sleep ignore sigchld + * From: Linus Torvalds <torvalds@transmeta.com> + * Date: Mon, 16 Nov 1998 11:02:15 -0800 (PST) + * + * On Mon, 16 Nov 1998, H. J. Lu wrote: + * > That is a kernel bug. SIGCHLD is a special one. Usually it cannot + * > be ignored. [snip...] + * + * No can do. + * + * "nanosleep()" is implemented in a bad way that makes it impossible to + * restart it cleanly. It was done that way because glibc wanted it that way, + * not because it's a good idea. [snip...] + * ---------- + * I assume that in the passed twelve+ years, nanosleep got fixed, + * but the hack in sleep to work around broken nanosleep was never removed. + */ + +# if 0 + /* This is a quick and dirty, but not 100% compliant with * the stupid SysV SIGCHLD vs. SIG_IGN behaviour. It is * fine unless you are messing with SIGCHLD... */ @@ -41,7 +84,7 @@ unsigned int sleep (unsigned int sec) return res; } -#else +# else /* We are going to use the `nanosleep' syscall of the kernel. But the kernel does not implement the sstupid SysV SIGCHLD vs. SIG_IGN @@ -49,64 +92,54 @@ unsigned int sleep (unsigned int sec) unsigned int sleep (unsigned int seconds) { struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 }; - sigset_t set, oset; + sigset_t set; + struct sigaction oact; unsigned int result; /* This is not necessary but some buggy programs depend on this. */ - if (seconds == 0) - { -# ifdef CANCELLATION_P - CANCELLATION_P (THREAD_SELF); -# endif - return 0; - } + if (seconds == 0) { +# ifdef CANCELLATION_P + CANCELLATION_P (THREAD_SELF); +# endif + return 0; + } /* Linux will wake up the system call, nanosleep, when SIGCHLD arrives even if SIGCHLD is ignored. We have to deal with it - in libc. We block SIGCHLD first. */ + in libc. */ + __sigemptyset (&set); __sigaddset (&set, SIGCHLD); - sigprocmask (SIG_BLOCK, &set, &oset); /* can't fail */ - /* If SIGCHLD is already blocked, we don't have to do anything. */ - if (!__sigismember (&oset, SIGCHLD)) - { - int saved_errno; - struct sigaction oact; - - __sigemptyset (&set); - __sigaddset (&set, SIGCHLD); - - sigaction (SIGCHLD, NULL, &oact); /* never fails */ - - if (oact.sa_handler == SIG_IGN) - { - /* We should leave SIGCHLD blocked. */ - result = nanosleep (&ts, &ts); - - saved_errno = errno; - /* Restore the original signal mask. */ - sigprocmask (SIG_SETMASK, &oset, NULL); - __set_errno (saved_errno); - } - else - { - /* We should unblock SIGCHLD. Restore the original signal mask. */ - sigprocmask (SIG_SETMASK, &oset, NULL); - result = nanosleep (&ts, &ts); - } + /* Is SIGCHLD set to SIG_IGN? */ + sigaction (SIGCHLD, NULL, &oact); /* never fails */ + if (oact.sa_handler == SIG_IGN) { + /* Yes. Block SIGCHLD, save old mask. */ + sigprocmask (SIG_BLOCK, &set, &set); /* never fails */ } - else - result = nanosleep (&ts, &ts); - if (result != 0) - /* Round remaining time. */ + /* Run nanosleep, with SIGCHLD blocked if SIGCHLD is SIG_IGNed. */ + result = nanosleep (&ts, &ts); + if (result != 0) { + /* Got EINTR. Return remaining time. */ result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); + } + + if (!__sigismember (&set, SIGCHLD)) { + /* We did block SIGCHLD, and old mask had no SIGCHLD bit. + IOW: we need to unblock SIGCHLD now. Do it. */ + /* this sigprocmask call never fails, thus never updates errno, + and therefore we don't need to save/restore it. */ + sigprocmask (SIG_SETMASK, &set, NULL); /* never fails */ + } return result; } -#endif + +# endif + #else /* __UCLIBC_HAS_REALTIME__ */ + /* no nanosleep, use signals and alarm() */ static void sleep_alarm_handler(int attribute_unused sig) { @@ -154,5 +187,7 @@ unsigned int sleep (unsigned int seconds) return result > seconds ? 0 : seconds - result; } + #endif /* __UCLIBC_HAS_REALTIME__ */ + libc_hidden_def(sleep) |