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