xref: /haiku/src/system/libroot/posix/time/clock_support.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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 <time_private.h>
18 #include <syscall_utils.h>
19 
20 #include <syscalls.h>
21 
22 
23 int
24 clock_getres(clockid_t clockID, struct timespec* resolution)
25 {
26 	// check the clock ID
27 	switch (clockID) {
28 		case CLOCK_MONOTONIC:
29 		case CLOCK_REALTIME:
30 		case CLOCK_PROCESS_CPUTIME_ID:
31 		case CLOCK_THREAD_CPUTIME_ID:
32 			break;
33 		default:
34 			if (clockID < 0)
35 				RETURN_AND_SET_ERRNO(EINVAL);
36 
37 			// For clock IDs we can't otherwise verify, try to get the time.
38 			if (clockID != getpid()) {
39 				timespec dummy;
40 				if (clock_gettime(clockID, &dummy) != 0)
41 					return -1;
42 			}
43 	}
44 
45 	// currently resolution is always 1us
46 	if (resolution != NULL) {
47 		resolution->tv_sec = 0;
48 		resolution->tv_nsec = 1000;
49 	}
50 
51 	return 0;
52 }
53 
54 
55 int
56 clock_gettime(clockid_t clockID, struct timespec* time)
57 {
58 	// get the time in microseconds
59 	bigtime_t microSeconds;
60 
61 	switch (clockID) {
62 		case CLOCK_MONOTONIC:
63 			microSeconds = system_time();
64 			break;
65 		case CLOCK_REALTIME:
66 			microSeconds = real_time_clock_usecs();
67 			break;
68 		case CLOCK_PROCESS_CPUTIME_ID:
69 		case CLOCK_THREAD_CPUTIME_ID:
70 		default:
71 		{
72 			status_t error = _kern_get_clock(clockID, &microSeconds);
73 			if (error != B_OK)
74 				RETURN_AND_SET_ERRNO(error);
75 		}
76 	}
77 
78 	// set the result
79 	time->tv_sec = microSeconds / 1000000;
80 	time->tv_nsec = (microSeconds % 1000000) * 1000;
81 
82 	return 0;
83 }
84 
85 
86 int
87 clock_settime(clockid_t clockID, const struct timespec* time)
88 {
89 	// can't set the monotonic clock
90 	if (clockID == CLOCK_MONOTONIC)
91 		RETURN_AND_SET_ERRNO(EINVAL);
92 
93 	// check timespec validity
94 	if (time->tv_sec < 0 || time->tv_nsec < 0 || time->tv_nsec >= 1000000000)
95 		RETURN_AND_SET_ERRNO(EINVAL);
96 
97 	// convert to microseconds and set the clock
98 	bigtime_t microSeconds = (bigtime_t)time->tv_sec * 1000000
99 		+ time->tv_nsec / 1000;
100 
101 	RETURN_AND_SET_ERRNO(_kern_set_clock(clockID, microSeconds));
102 }
103 
104 
105 int
106 clock_nanosleep(clockid_t clockID, int flags, const struct timespec* time,
107 	struct timespec* remainingTime)
108 {
109 	// convert time to microseconds (round up)
110 	bigtime_t microSeconds;
111 	if (!timespec_to_bigtime(*time, microSeconds))
112 		RETURN_AND_TEST_CANCEL(EINVAL);
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