Line data Source code
1 : /* Provide a replacement for the POSIX nanosleep function.
2 :
3 : Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006, 2007 Free
4 : Software Foundation, Inc.
5 :
6 : This program is free software: you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program 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
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : /* written by Jim Meyering */
20 :
21 : #include <config.h>
22 :
23 : #include <time.h>
24 :
25 : #include "timespec.h"
26 :
27 : #include <stdbool.h>
28 : #include <stdio.h>
29 : #include <sys/types.h>
30 : #if HAVE_SYS_SELECT_H
31 : # include <sys/select.h>
32 : #endif
33 : #include <signal.h>
34 :
35 : #include <sys/time.h>
36 : #include <errno.h>
37 :
38 : #include <unistd.h>
39 :
40 : #undef nanosleep
41 :
42 : enum { BILLION = 1000 * 1000 * 1000 };
43 :
44 : #if HAVE_BUG_BIG_NANOSLEEP
45 :
46 : void
47 2 : getnow (struct timespec *t)
48 : {
49 : # if defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME
50 2 : if (clock_gettime (CLOCK_MONOTONIC, t) == 0)
51 2 : return;
52 : # endif
53 0 : gettime (t);
54 : }
55 :
56 : int
57 1 : rpl_nanosleep (const struct timespec *requested_delay,
58 : struct timespec *remaining_delay)
59 : {
60 : /* nanosleep mishandles large sleeps due to internal overflow
61 : problems, so check that the proper amount of time has actually
62 : elapsed. */
63 :
64 1 : struct timespec delay = *requested_delay;
65 : struct timespec t0;
66 1 : getnow (&t0);
67 :
68 : for (;;)
69 0 : {
70 1 : int r = nanosleep (&delay, remaining_delay);
71 1 : if (r == 0)
72 : {
73 : time_t secs_sofar;
74 : struct timespec now;
75 1 : getnow (&now);
76 :
77 1 : secs_sofar = now.tv_sec - t0.tv_sec;
78 1 : if (requested_delay->tv_sec < secs_sofar)
79 1 : return 0;
80 1 : delay.tv_sec = requested_delay->tv_sec - secs_sofar;
81 1 : delay.tv_nsec = requested_delay->tv_nsec - (now.tv_nsec - t0.tv_nsec);
82 1 : if (delay.tv_nsec < 0)
83 : {
84 1 : if (delay.tv_sec == 0)
85 1 : return 0;
86 0 : delay.tv_nsec += BILLION;
87 0 : delay.tv_sec--;
88 : }
89 0 : else if (BILLION <= delay.tv_nsec)
90 : {
91 0 : delay.tv_nsec -= BILLION;
92 0 : delay.tv_sec++;
93 : }
94 : }
95 : }
96 : }
97 :
98 : #else
99 :
100 : /* Some systems (MSDOS) don't have SIGCONT.
101 : Using SIGTERM here turns the signal-handling code below
102 : into a no-op on such systems. */
103 : # ifndef SIGCONT
104 : # define SIGCONT SIGTERM
105 : # endif
106 :
107 : # if ! HAVE_SIGINTERRUPT
108 : # define siginterrupt(sig, flag) /* empty */
109 : # endif
110 :
111 : static sig_atomic_t volatile suspended;
112 :
113 : /* Handle SIGCONT. */
114 :
115 : static void
116 : sighandler (int sig)
117 : {
118 : suspended = 1;
119 : }
120 :
121 : /* Suspend execution for at least *TS_DELAY seconds. */
122 :
123 : static void
124 : my_usleep (const struct timespec *ts_delay)
125 : {
126 : struct timeval tv_delay;
127 : tv_delay.tv_sec = ts_delay->tv_sec;
128 : tv_delay.tv_usec = (ts_delay->tv_nsec + 999) / 1000;
129 : if (tv_delay.tv_usec == 1000000)
130 : {
131 : time_t t1 = tv_delay.tv_sec + 1;
132 : if (t1 < tv_delay.tv_sec)
133 : tv_delay.tv_usec = 1000000 - 1; /* close enough */
134 : else
135 : {
136 : tv_delay.tv_sec = t1;
137 : tv_delay.tv_usec = 0;
138 : }
139 : }
140 : select (0, NULL, NULL, NULL, &tv_delay);
141 : }
142 :
143 : /* Suspend execution for at least *REQUESTED_DELAY seconds. The
144 : *REMAINING_DELAY part isn't implemented yet. */
145 :
146 : int
147 : rpl_nanosleep (const struct timespec *requested_delay,
148 : struct timespec *remaining_delay)
149 : {
150 : static bool initialized;
151 :
152 : /* set up sig handler */
153 : if (! initialized)
154 : {
155 : # ifdef SA_NOCLDSTOP
156 : struct sigaction oldact, newact;
157 : newact.sa_handler = sighandler;
158 : sigemptyset (&newact.sa_mask);
159 : newact.sa_flags = 0;
160 :
161 : sigaction (SIGCONT, NULL, &oldact);
162 : if (oldact.sa_handler != SIG_IGN)
163 : sigaction (SIGCONT, &newact, NULL);
164 : # else
165 : if (signal (SIGCONT, SIG_IGN) != SIG_IGN)
166 : {
167 : signal (SIGCONT, sighandler);
168 : siginterrupt (SIGCONT, 1);
169 : }
170 : # endif
171 : initialized = true;
172 : }
173 :
174 : suspended = 0;
175 :
176 : my_usleep (requested_delay);
177 :
178 : if (suspended)
179 : {
180 : /* Calculate time remaining. */
181 : /* FIXME: the code in sleep doesn't use this, so there's no
182 : rush to implement it. */
183 :
184 : errno = EINTR;
185 : }
186 :
187 : /* FIXME: Restore sig handler? */
188 :
189 : return suspended;
190 : }
191 : #endif
|