blob: 9db115a29579fe3d6dd33af41faf6ab746d9c1a9 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* Implementation of the POSIX sleep function using nanosleep.
2 Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21#include <errno.h>
22#include <time.h>
23#include <signal.h>
24#include <unistd.h>
25
26
27/* version perusing nanosleep */
28#if defined __UCLIBC_HAS_REALTIME__
29
30/* I am unable to reproduce alleged "Linux quirk".
31 * I used the following test program:
32#include <unistd.h>
33#include <time.h>
34#include <signal.h>
35static void dummy(int sig) {}
36int main() {
37 struct timespec t = { 2, 0 };
38 if (fork() == 0) {
39 sleep(1);
40 return 0;
41 }
42 signal(SIGCHLD, SIG_DFL); //
43 signal(SIGCHLD, dummy); // Pick one
44 signal(SIGCHLD, SIG_IGN); //
45 nanosleep(&t, &t);
46 return 0;
47}
48 * Testing on 2.4.20 and on 2.6.35-rc4:
49 * With SIG_DFL, nanosleep is not interrupted by SIGCHLD. Ok.
50 * With dummy handler, nanosleep is interrupted by SIGCHLD. Ok.
51 * With SIG_IGN, nanosleep is NOT interrupted by SIGCHLD.
52 * It looks like sleep's workaround for SIG_IGN is no longer needed?
53 * The only emails I can find are from 1998 (!):
54 * ----------
55 * Subject: Re: sleep ignore sigchld
56 * From: Linus Torvalds <torvalds@transmeta.com>
57 * Date: Mon, 16 Nov 1998 11:02:15 -0800 (PST)
58 *
59 * On Mon, 16 Nov 1998, H. J. Lu wrote:
60 * > That is a kernel bug. SIGCHLD is a special one. Usually it cannot
61 * > be ignored. [snip...]
62 *
63 * No can do.
64 *
65 * "nanosleep()" is implemented in a bad way that makes it impossible to
66 * restart it cleanly. It was done that way because glibc wanted it that way,
67 * not because it's a good idea. [snip...]
68 * ----------
69 * I assume that in the passed twelve+ years, nanosleep got fixed,
70 * but the hack in sleep to work around broken nanosleep was never removed.
71 */
72
73# if 0
74
75/* This is a quick and dirty, but not 100% compliant with
76 * the stupid SysV SIGCHLD vs. SIG_IGN behaviour. It is
77 * fine unless you are messing with SIGCHLD... */
78unsigned int sleep (unsigned int sec)
79{
80 unsigned int res;
81 struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 };
82 res = nanosleep(&ts, &ts);
83 if (res) res = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L);
84 return res;
85}
86
87# else
88
89/* We are going to use the `nanosleep' syscall of the kernel. But the
90 kernel does not implement the sstupid SysV SIGCHLD vs. SIG_IGN
91 behaviour for this syscall. Therefore we have to emulate it here. */
92unsigned int sleep (unsigned int seconds)
93{
94 struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 };
95 sigset_t set;
96 struct sigaction oact;
97 unsigned int result;
98
99 /* This is not necessary but some buggy programs depend on this. */
100 if (seconds == 0) {
101# ifdef CANCELLATION_P
102 CANCELLATION_P (THREAD_SELF);
103# endif
104 return 0;
105 }
106
107 /* Linux will wake up the system call, nanosleep, when SIGCHLD
108 arrives even if SIGCHLD is ignored. We have to deal with it
109 in libc. */
110
111 __sigemptyset (&set);
112 __sigaddset (&set, SIGCHLD);
113
114 /* Is SIGCHLD set to SIG_IGN? */
115 sigaction (SIGCHLD, NULL, &oact); /* never fails */
116 if (oact.sa_handler == SIG_IGN) {
117 /* Yes. Block SIGCHLD, save old mask. */
118 sigprocmask (SIG_BLOCK, &set, &set); /* never fails */
119 }
120
121 /* Run nanosleep, with SIGCHLD blocked if SIGCHLD is SIG_IGNed. */
122 result = nanosleep (&ts, &ts);
123 if (result != 0) {
124 /* Got EINTR. Return remaining time. */
125 result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L);
126 }
127
128 if (!__sigismember (&set, SIGCHLD)) {
129 /* We did block SIGCHLD, and old mask had no SIGCHLD bit.
130 IOW: we need to unblock SIGCHLD now. Do it. */
131 /* this sigprocmask call never fails, thus never updates errno,
132 and therefore we don't need to save/restore it. */
133 sigprocmask (SIG_SETMASK, &set, NULL); /* never fails */
134 }
135
136 return result;
137}
138
139# endif
140
141#else /* __UCLIBC_HAS_REALTIME__ */
142
143/* no nanosleep, use signals and alarm() */
144static void sleep_alarm_handler(int attribute_unused sig)
145{
146}
147unsigned int sleep (unsigned int seconds)
148{
149 struct sigaction act, oact;
150 sigset_t set, oset;
151 unsigned int result, remaining;
152 time_t before, after;
153 int old_errno = errno;
154
155 /* This is not necessary but some buggy programs depend on this. */
156 if (seconds == 0)
157 return 0;
158
159 /* block SIGALRM */
160 __sigemptyset (&set);
161 __sigaddset (&set, SIGALRM);
162 sigprocmask (SIG_BLOCK, &set, &oset); /* can't fail */
163
164 act.sa_handler = sleep_alarm_handler;
165 act.sa_flags = 0;
166 act.sa_mask = oset;
167 sigaction(SIGALRM, &act, &oact); /* never fails */
168
169 before = time(NULL);
170 remaining = alarm(seconds);
171 if (remaining && remaining > seconds) {
172 /* restore user's alarm */
173 sigaction(SIGALRM, &oact, NULL);
174 alarm(remaining); /* restore old alarm */
175 sigsuspend(&oset);
176 after = time(NULL);
177 } else {
178 sigsuspend (&oset);
179 after = time(NULL);
180 sigaction (SIGALRM, &oact, NULL);
181 }
182 result = after - before;
183 alarm(remaining > result ? remaining - result : 0);
184 sigprocmask (SIG_SETMASK, &oset, NULL);
185
186 __set_errno(old_errno);
187
188 return result > seconds ? 0 : seconds - result;
189}
190
191#endif /* __UCLIBC_HAS_REALTIME__ */
192
193libc_hidden_def(sleep)