xref: /haiku/src/system/libroot/posix/time/clock_support.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <time.h>
8 
9 #include <errno.h>
10 #include <pthread.h>
11 #include <sys/resource.h>
12 #include <unistd.h>
13 
14 #include <OS.h>
15 
16 #include <errno_private.h>
17 #include <syscall_utils.h>
18 
19 #include <syscalls.h>
20 
21 
22 int
23 clock_getres(clockid_t clockID, struct timespec* resolution)
24 {
25 	// check the clock ID
26 	switch (clockID) {
27 		case CLOCK_MONOTONIC:
28 		case CLOCK_REALTIME:
29 		case CLOCK_PROCESS_CPUTIME_ID:
30 		case CLOCK_THREAD_CPUTIME_ID:
31 			break;
32 		default:
33 			if (clockID < 0)
34 				RETURN_AND_SET_ERRNO(EINVAL);
35 
36 			// For clock IDs we can't otherwise verify, try to get the time.
37 			if (clockID != getpid()) {
38 				timespec dummy;
39 				if (clock_gettime(clockID, &dummy) != 0)
40 					return -1;
41 			}
42 	}
43 
44 	// currently resolution is always 1us
45 	if (resolution != NULL) {
46 		resolution->tv_sec = 0;
47 		resolution->tv_nsec = 1000;
48 	}
49 
50 	return 0;
51 }
52 
53 
54 int
55 clock_gettime(clockid_t clockID, struct timespec* time)
56 {
57 	// get the time in microseconds
58 	bigtime_t microSeconds;
59 
60 	switch (clockID) {
61 		case CLOCK_MONOTONIC:
62 			microSeconds = system_time();
63 			break;
64 		case CLOCK_REALTIME:
65 			microSeconds = real_time_clock_usecs();
66 			break;
67 		case CLOCK_PROCESS_CPUTIME_ID:
68 		case CLOCK_THREAD_CPUTIME_ID:
69 		default:
70 		{
71 			status_t error = _kern_get_clock(clockID, &microSeconds);
72 			if (error != B_OK)
73 				RETURN_AND_SET_ERRNO(error);
74 		}
75 	}
76 
77 	// set the result
78 	time->tv_sec = microSeconds / 1000000;
79 	time->tv_nsec = (microSeconds % 1000000) * 1000;
80 
81 	return 0;
82 }
83 
84 
85 int
86 clock_settime(clockid_t clockID, const struct timespec* time)
87 {
88 	// can't set the monotonic clock
89 	if (clockID == CLOCK_MONOTONIC)
90 		RETURN_AND_SET_ERRNO(EINVAL);
91 
92 	// check timespec validity
93 	if (time->tv_sec < 0 || time->tv_nsec < 0 || time->tv_nsec >= 1000000000)
94 		RETURN_AND_SET_ERRNO(EINVAL);
95 
96 	// convert to microseconds and set the clock
97 	bigtime_t microSeconds = (bigtime_t)time->tv_sec * 1000000
98 		+ time->tv_nsec / 1000;
99 
100 	RETURN_AND_SET_ERRNO(_kern_set_clock(clockID, microSeconds));
101 }
102 
103 
104 int
105 clock_nanosleep(clockid_t clockID, int flags, const struct timespec* time,
106 	struct timespec* remainingTime)
107 {
108 	// convert time to microseconds (round up)
109 	if (time->tv_sec < 0 || time->tv_nsec < 0 || time->tv_nsec >= 1000000000)
110 		RETURN_AND_TEST_CANCEL(EINVAL);
111 
112 	bigtime_t microSeconds = (bigtime_t)time->tv_sec * 1000000
113 		+ (time->tv_nsec + 999) / 1000;
114 
115 	// get timeout flags
116 	uint32 timeoutFlags;
117 	if ((flags & TIMER_ABSTIME) != 0) {
118 		timeoutFlags = B_ABSOLUTE_TIMEOUT;
119 
120 		// ignore remainingTime for absolute waits
121 		remainingTime = NULL;
122 	} else
123 		timeoutFlags = B_RELATIVE_TIMEOUT;
124 
125 	// wait
126 	bigtime_t remainingMicroSeconds;
127 	status_t error = _kern_snooze_etc(microSeconds, clockID, timeoutFlags,
128 		remainingTime != NULL ? &remainingMicroSeconds : NULL);
129 
130 	// If interrupted and this is a relative wait, compute and return the
131 	// remaining wait time.
132 	if (error == B_INTERRUPTED && remainingTime != NULL) {
133 		if (remainingMicroSeconds > 0) {
134 			remainingTime->tv_sec = remainingMicroSeconds / 1000000;
135 			remainingTime->tv_nsec = (remainingMicroSeconds % 1000000) * 1000;
136 		} else {
137 			// We were slow enough that the wait time passed anyway.
138 			error = B_OK;
139 		}
140 	}
141 
142 	RETURN_AND_TEST_CANCEL(error);
143 }
144 
145 
146 int
147 clock_getcpuclockid(pid_t pid, clockid_t* _clockID)
148 {
149 	if (pid < 0)
150 		return ESRCH;
151 
152 	// The CPU clock ID for a process is simply the team ID. For pid == 0 we're
153 	// supposed to return the current process' clock.
154 	if (pid == 0) {
155 		*_clockID = getpid();
156 		return 0;
157 	}
158 
159 	// test-get the time to verify the team exists and we have permission
160 	bigtime_t microSeconds;
161 	status_t error = _kern_get_clock(pid, &microSeconds);
162 	if (error != B_OK) {
163 		// Since pid is > 0, B_BAD_VALUE always means a team with that ID
164 		// doesn't exist. Translate the error code accordingly.
165 		if (error == B_BAD_VALUE)
166 			return ESRCH;
167 		return error;
168 	}
169 
170 	*_clockID = pid;
171 	return 0;
172 }
173