summaryrefslogtreecommitdiff
path: root/libc/unistd/sleep.c
diff options
context:
space:
mode:
authorCarmelo Amoroso <carmelo.amoroso@st.com>2010-11-28 21:09:47 +0100
committerCarmelo Amoroso <carmelo.amoroso@st.com>2010-11-28 21:09:47 +0100
commit57f6cce72c889f2e7b342e87a565a064320a6ee2 (patch)
tree5466406472918d3adb63398f5fb9ea1511a5e73f /libc/unistd/sleep.c
parent4ec89b87bc0eea8d9ca6b50564d12eeb3b1b0119 (diff)
parent5dea871ac73ef4608022c058b50adc946917f9b9 (diff)
Merge commit 'origin/master' into prelink
Conflicts: ldso/include/dl-hash.h Signed-off-by: Carmelo Amoroso <carmelo.amoroso@st.com>
Diffstat (limited to 'libc/unistd/sleep.c')
-rw-r--r--libc/unistd/sleep.c125
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)