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
22int
23clock_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
54int
55clock_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
85int
86clock_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
104int
105clock_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
146int
147clock_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