xref: /openbsd-src/regress/sys/kern/nanosleep/nanosleep.c (revision 625a509b4972da8282e87b2961c9b12c87a35de9)
1 /*	$OpenBSD: nanosleep.c,v 1.9 2024/02/29 21:47:02 bluhm Exp $	*/
2 /*
3  *	Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
4  */
5 #include <sys/types.h>
6 #include <sys/time.h>
7 #include <sys/wait.h>
8 
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <time.h>
14 #include <err.h>
15 #include <signal.h>
16 
17 int invalid_time(void);
18 int trivial(void);
19 int with_signal(void);
20 int time_elapsed(void);
21 int time_elapsed_with_signal(void);
22 
23 int short_time(void);
24 
25 void sighandler(int);
26 
27 int
main(int argc,char ** argv)28 main(int argc, char **argv)
29 {
30 	int ch, ret;
31 
32 	ret = 0;
33 
34 	while ((ch = getopt(argc, argv, "itseES")) != -1) {
35 		switch (ch) {
36 		case 'i':
37 			ret |= invalid_time();
38 			break;
39 		case 't':
40 			ret |= trivial();
41 			break;
42 		case 's':
43 			ret |= with_signal();
44 			break;
45 		case 'e':
46 			ret |= time_elapsed();
47 			break;
48 		case 'E':
49 			ret |= time_elapsed_with_signal();
50 			break;
51 		case 'S':
52 			ret |= short_time();
53 			break;
54 		default:
55 			fprintf(stderr, "Usage: nanosleep [-itseSE]\n");
56 			exit(1);
57 		}
58 	}
59 
60 	return (ret);
61 }
62 
63 void
sighandler(int signum)64 sighandler(int signum)
65 {
66 }
67 
68 int
trivial(void)69 trivial(void)
70 {
71 	struct timespec timeout, remainder;
72 
73 	timeout.tv_sec = 0;
74 	timeout.tv_nsec = 30000000;
75 	remainder.tv_sec = 4711;	/* Just add to the confusion */
76 	remainder.tv_nsec = 4711;
77 	if (nanosleep(&timeout, &remainder) < 0) {
78 		warn("%s: nanosleep", __func__);
79 		return 1;
80 	}
81 
82 	/*
83 	 * Just check that we don't get any leftover time if we sleep the
84 	 * amount of time we want to sleep.
85 	 * If we receive any signal, something is wrong anyway.
86 	 */
87 	if (remainder.tv_sec != 0 || remainder.tv_nsec != 0) {
88 		warnx("%s: non-zero time: %lld.%09ld", __func__,
89 		    (long long)remainder.tv_sec, remainder.tv_nsec);
90 		return 1;
91 	}
92 
93 	return 0;
94 }
95 
96 int
with_signal(void)97 with_signal(void)
98 {
99 	struct timespec timeout, remainder;
100 	pid_t pid;
101 	int status;
102 
103 	signal(SIGUSR1, sighandler);
104 
105 	pid = getpid();
106 
107 	switch(fork()) {
108 	case -1:
109 		err(1, "fork");
110 	case 0:
111 		timeout.tv_sec = 1;
112 		timeout.tv_nsec = 0;
113 		nanosleep(&timeout, NULL);
114 		kill(pid, SIGUSR1);
115 		_exit(0);
116 	default:
117 		break;
118 	}
119 
120 	timeout.tv_sec = 10;
121 	timeout.tv_nsec = 0;
122 	remainder.tv_sec = 0;
123 	remainder.tv_nsec = 0;
124 	if (nanosleep(&timeout, &remainder) == 0) {
125 		warnx("%s: nanosleep", __func__);
126 		return 1;
127 	}
128 
129 	if (remainder.tv_sec == 0 && remainder.tv_nsec == 0) {
130 		warnx("%s: zero time", __func__);
131 		return 1;
132 	}
133 
134 	if (wait(&status) < 0)
135 		err(1, "wait");
136 	if (status != 0)
137 		errx(1, "status");
138 
139 	return 0;
140 }
141 
142 int
time_elapsed(void)143 time_elapsed(void)
144 {
145 	struct timespec timeout;
146 	struct timespec start, end, duration;
147 
148 	timeout.tv_sec = 0;
149 	timeout.tv_nsec = 500000000;
150 
151 	if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
152 		warn("%s: clock_gettime", __func__);
153 		return 1;
154 	}
155 
156 	if (nanosleep(&timeout, NULL) < 0) {
157 		warn("%s: nanosleep", __func__);
158 		return 1;
159 	}
160 
161 	if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
162 		warn("%s: clock_gettime", __func__);
163 		return 1;
164 	}
165 
166 	timespecsub(&end, &start, &duration);
167 
168 	if (duration.tv_sec == 0 && duration.tv_nsec < 500000000) {
169 		warnx("%s: slept less than 0.5 sec: %lld.%09ld", __func__,
170 		    (long long)duration.tv_sec, duration.tv_nsec);
171 		return 1;
172 	}
173 
174 	return 0;
175 }
176 
177 int
time_elapsed_with_signal(void)178 time_elapsed_with_signal(void)
179 {
180 	struct timespec timeout, remainder;
181 	struct timespec start, end, duration;
182 	pid_t pid;
183 	int status;
184 
185 	signal(SIGUSR1, sighandler);
186 
187 	pid = getpid();
188 
189 	switch(fork()) {
190 	case -1:
191 		err(1, "fork");
192 	case 0:
193 		timeout.tv_sec = 1;
194 		timeout.tv_nsec = 0;
195 		nanosleep(&timeout, NULL);
196 		kill(pid, SIGUSR1);
197 		_exit(0);
198 	default:
199 		break;
200 	}
201 
202 	if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
203 		warn("%s: clock_gettime", __func__);
204 		return 1;
205 	}
206 
207 	timeout.tv_sec = 10;
208 	timeout.tv_nsec = 0;
209 	remainder.tv_sec = 0;
210 	remainder.tv_nsec = 0;
211 	if (nanosleep(&timeout, &remainder) == 0) {
212 		warnx("%s: nanosleep", __func__);
213 		return 1;
214 	}
215 
216 	if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
217 		warn("%s: clock_gettime", __func__);
218 		return 1;
219 	}
220 
221 	timespecsub(&end, &start, &duration);
222 	timespecadd(&duration, &remainder, &timeout);
223 	/* XXX remainder may be one tick too small */
224 	remainder.tv_sec = 0;
225 	remainder.tv_nsec = 10000000;
226 	timespecadd(&timeout, &remainder, &timeout);
227 
228 	if (timeout.tv_sec < 10) {
229 		warnx("%s: slept time + leftover time < 10 sec: %lld.%09ld",
230 		    __func__, (long long)timeout.tv_sec, timeout.tv_nsec);
231 		return 1;
232 	}
233 
234 	if (wait(&status) < 0)
235 		err(1, "wait");
236 	if (status != 0)
237 		errx(1, "status");
238 
239 	return 0;
240 }
241 
242 int
short_time(void)243 short_time(void)
244 {
245 	struct timespec timeout;
246 	pid_t pid;
247 	int status;
248 
249 	signal(SIGUSR1, sighandler);
250 
251 	pid = getpid();
252 
253 	switch(fork()) {
254 	case -1:
255 		err(1, "fork");
256 	case 0:
257 		/* Sleep two seconds, then shoot parent. */
258 		timeout.tv_sec = 2;
259 		timeout.tv_nsec = 0;
260 		nanosleep(&timeout, NULL);
261 		kill(pid, SIGUSR1);
262 		_exit(0);
263 	default:
264 		break;
265 	}
266 
267 	timeout.tv_sec = 0;
268 	timeout.tv_nsec = 1;
269 	if (nanosleep(&timeout, NULL) < 0) {
270 		warn("%s: nanosleep", __func__);
271 		return 1;
272 	}
273 
274 	if (wait(&status) < 0)
275 		err(1, "wait");
276 	if (status != 0)
277 		errx(1, "status");
278 
279 	return 0;
280 }
281 
282 int
invalid_time(void)283 invalid_time(void)
284 {
285 	struct timespec timeout[3] = { {-1, 0}, {0, -1}, {0, 1000000000L} };
286 	int i, status;
287 
288 	for (i = 0; i < 3; i++) {
289 		status = nanosleep(&timeout[i], NULL);
290 		if (status != -1 || errno != EINVAL) {
291 			warnx("%s: nanosleep %lld %ld", __func__,
292 			    (long long)timeout[i].tv_sec, timeout[i].tv_nsec);
293 			return 1;
294 		}
295 	}
296 	return 0;
297 }
298