1 /* xnanosleep.c -- a more convenient interface to nanosleep
2 Copyright (C) 2002, 2003 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Mostly written (for sleep.c) by Paul Eggert.
19 Factored out (creating this file) by Jim Meyering. */
28 #include <sys/types.h>
35 /* The extra casts work around common compiler bugs. */
36 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
37 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
38 It is necessary at least when t == time_t. */
39 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
40 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
41 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
44 # define TIME_T_MAX TYPE_MAXIMUM (time_t)
49 #include "xnanosleep.h"
51 /* Subtract the `struct timespec' values X and Y by computing X - Y.
52 If the difference is negative or zero, return 0.
53 Otherwise, return 1 and store the difference in DIFF.
54 X and Y must have valid ts_nsec values, in the range 0 to 999999999.
55 If the difference would overflow, store the maximum possible difference. */
58 timespec_subtract (struct timespec
*diff
,
59 struct timespec
const *x
, struct timespec
const *y
)
61 time_t sec
= x
->tv_sec
- y
->tv_sec
;
62 long int nsec
= x
->tv_nsec
- y
->tv_nsec
;
64 if (x
->tv_sec
< y
->tv_sec
)
69 /* The difference has overflowed. */
73 else if (sec
== 0 && nsec
<= 0)
87 /* Sleep until the time (call it WAKE_UP_TIME) specified as
88 SECONDS seconds after the time this function is called.
89 SECONDS must be non-negative. If SECONDS is so large that
90 it is not representable as a `struct timespec', then use
91 the maximum value for that interval. Return -1 on failure
92 (setting errno), 0 on success. */
95 xnanosleep (double seconds
)
99 struct timespec ts_start
;
100 struct timespec ts_sleep
;
101 struct timespec ts_stop
;
103 assert (0 <= seconds
);
105 if (gettime (&ts_start
) != 0)
108 /* Separate whole seconds from nanoseconds.
109 Be careful to detect any overflow. */
110 ts_sleep
.tv_sec
= seconds
;
111 ns
= 1e9
* (seconds
- ts_sleep
.tv_sec
);
112 overflow
= ! (ts_sleep
.tv_sec
<= seconds
&& 0 <= ns
&& ns
<= 1e9
);
113 ts_sleep
.tv_nsec
= ns
;
115 /* Round up to the next whole number, if necessary, so that we
116 always sleep for at least the requested amount of time. Assuming
117 the default rounding mode, we don't have to worry about the
118 rounding error when computing 'ns' above, since the error won't
119 cause 'ns' to drop below an integer boundary. */
120 ts_sleep
.tv_nsec
+= (ts_sleep
.tv_nsec
< ns
);
122 /* Normalize the interval length. nanosleep requires this. */
123 if (1000000000 <= ts_sleep
.tv_nsec
)
125 time_t t
= ts_sleep
.tv_sec
+ 1;
127 /* Detect integer overflow. */
128 overflow
|= (t
< ts_sleep
.tv_sec
);
131 ts_sleep
.tv_nsec
-= 1000000000;
134 /* Compute the time until which we should sleep. */
135 ts_stop
.tv_sec
= ts_start
.tv_sec
+ ts_sleep
.tv_sec
;
136 ts_stop
.tv_nsec
= ts_start
.tv_nsec
+ ts_sleep
.tv_nsec
;
137 if (1000000000 <= ts_stop
.tv_nsec
)
140 ts_stop
.tv_nsec
-= 1000000000;
143 /* Detect integer overflow. */
144 overflow
|= (ts_stop
.tv_sec
< ts_start
.tv_sec
145 || (ts_stop
.tv_sec
== ts_start
.tv_sec
146 && ts_stop
.tv_nsec
< ts_start
.tv_nsec
));
150 /* Fix ts_sleep and ts_stop, which may be garbage due to overflow. */
151 ts_sleep
.tv_sec
= ts_stop
.tv_sec
= TIME_T_MAX
;
152 ts_sleep
.tv_nsec
= ts_stop
.tv_nsec
= 999999999;
155 while (nanosleep (&ts_sleep
, NULL
) != 0)
157 if (errno
!= EINTR
|| gettime (&ts_start
) != 0)
160 /* POSIX.1-2001 requires that when a process is suspended, then
161 resumed, nanosleep (A, B) returns -1, sets errno to EINTR,
162 and sets *B to the time remaining at the point of resumption.
163 However, some versions of the Linux kernel incorrectly return
164 the time remaining at the point of suspension. Work around
165 this bug by computing the remaining time here, rather than by
166 relying on nanosleep's computation. */
168 if (! timespec_subtract (&ts_sleep
, &ts_stop
, &ts_start
))