summaryrefslogtreecommitdiff
path: root/minix/lib/libc/sys/nanosleep.c
blob: c69c4cf6831c20b965aeb966ca95169f076b92c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*	nanosleep() - Sleep for a number of seconds.	Author: Erik van der Kouwe
 *								25 July 2009
 */

#include <sys/cdefs.h>
#include "namespace.h"
#include <lib.h>

#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/select.h>
#include <sys/time.h>

#define MSEC_PER_SEC 1000
#define USEC_PER_MSEC 1000
#define NSEC_PER_USEC 1000

#define USEC_PER_SEC (USEC_PER_MSEC * MSEC_PER_SEC)
#define NSEC_PER_SEC (NSEC_PER_USEC * USEC_PER_SEC)

int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
	struct timeval timeout, timestart = { 0, 0 }, timeend;
	int errno_select, r;
	struct timespec rqt;

	/* check parameters */
	if (!rqtp) {
		errno = EFAULT;
		return -1;
	}

	if (rqtp->tv_sec < 0 || 
		rqtp->tv_nsec < 0 ||
		rqtp->tv_nsec >= NSEC_PER_SEC) {
		errno = EINVAL;
		return -1;
	}

	/* store *rqtp to make sure it is not overwritten */
	rqt = *rqtp;
	
	/* keep track of start time if needed */
	if (rmtp)
	{
		rmtp->tv_sec = 0;
		rmtp->tv_nsec = 0;
		if (gettimeofday(&timestart, NULL) < 0)
			return -1;
	}

	/* use select to wait */
	timeout.tv_sec = rqt.tv_sec;
	timeout.tv_usec = (rqt.tv_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC;
	r = select(0, NULL, NULL, NULL, &timeout);

	/* return remaining time only if requested */
	/* if select succeeded then we slept all time */
	if (!rmtp || r >= 0)
		return r;

	/* measure end time; preserve errno */
	errno_select = errno;
	if (gettimeofday(&timeend, NULL) < 0)
		return -1;

	errno = errno_select;

	/* compute remaining time */
	rmtp->tv_sec = rqt.tv_sec - (timeend.tv_sec - timestart.tv_sec);
	rmtp->tv_nsec = rqt.tv_nsec - (timeend.tv_usec - timestart.tv_usec) * NSEC_PER_USEC;

	/* bring remaining time into canonical form */
	while (rmtp->tv_nsec < 0)
	{
		rmtp->tv_sec -= 1;
		rmtp->tv_nsec += NSEC_PER_SEC;
	}

	while (rmtp->tv_nsec > NSEC_PER_SEC)
	{
		rmtp->tv_sec += 1;
		rmtp->tv_nsec -= NSEC_PER_SEC;
	}

	/* remaining time must not be negative */
	if (rmtp->tv_sec < 0)
	{
		rmtp->tv_sec = 0;
		rmtp->tv_nsec = 0;
	}

	return r;
}


#if defined(__minix) && defined(__weak_alias)
__weak_alias(nanosleep, __nanosleep50)
#endif