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
11#include <new>
12
13#include <AutoDeleter.h>
14#include <syscall_utils.h>
15
16#include <errno_private.h>
17#include <syscalls.h>
18#include <thread_defs.h>
19#include <user_timer_defs.h>
20
21#include <libroot_private.h>
22#include <pthread_private.h>
23#include <time_private.h>
24#include <user_thread.h>
25
26
27static void
28info_to_itimerspec(const user_timer_info& info, itimerspec& spec)
29{
30	bigtime_to_timespec(info.interval, spec.it_interval);
31
32	// A remaining_time of B_INFINITE_TIMEOUT means the timer isn't scheduled.
33	if (info.remaining_time != B_INFINITE_TIMEOUT) {
34		bigtime_to_timespec(info.remaining_time, spec.it_value);
35	} else {
36		spec.it_value.tv_sec = 0;
37		spec.it_value.tv_nsec = 0;
38	}
39}
40
41
42static bool
43itimerspec_to_bigtimes(const itimerspec& spec, bigtime_t& _nextTime,
44	bigtime_t& _interval)
45{
46	if (!timespec_to_bigtime(spec.it_interval, _interval)
47		|| !timespec_to_bigtime(spec.it_value, _nextTime)) {
48		return false;
49	}
50
51	if (_nextTime == 0)
52		_nextTime = B_INFINITE_TIMEOUT;
53
54	return true;
55}
56
57
58static status_t
59timer_thread_entry(void* _entry, void* _value)
60{
61	// init a pthread
62	pthread_thread thread;
63	__init_pthread(&thread, NULL, NULL);
64	thread.flags |= THREAD_DETACHED;
65
66	get_user_thread()->pthread = &thread;
67
68	// we have been started with deferred signals -- undefer them
69	undefer_signals();
70
71	// prepare the arguments
72	union sigval value;
73	value.sival_ptr = _value;
74	void (*entry)(union sigval) = (void (*)(union sigval))_entry;
75
76	// call the entry function
77	entry(value);
78
79	return B_OK;
80}
81
82
83// #pragma mark -
84
85
86int
87timer_create(clockid_t clockID, struct sigevent* event, timer_t* _timer)
88{
89	// create a timer object
90	__timer_t* timer = new(std::nothrow) __timer_t;
91	if (timer == NULL)
92		RETURN_AND_SET_ERRNO(ENOMEM);
93	ObjectDeleter<__timer_t> timerDeleter(timer);
94
95	// If the notification method is SIGEV_THREAD, initialize thread creation
96	// attributes.
97	bool isThreadEvent = event != NULL && event->sigev_notify == SIGEV_THREAD;
98	thread_creation_attributes threadAttributes;
99	if (isThreadEvent) {
100		status_t error = __pthread_init_creation_attributes(
101			event->sigev_notify_attributes, NULL, &timer_thread_entry,
102			(void*)event->sigev_notify_function, event->sigev_value.sival_ptr,
103			"timer notify", &threadAttributes);
104		if (error != B_OK)
105			RETURN_AND_SET_ERRNO(error);
106
107		threadAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS;
108	}
109
110	// create the timer
111	int32 timerID = _kern_create_timer(clockID, -1, 0, event,
112		isThreadEvent ? &threadAttributes : NULL);
113	if (timerID < 0)
114		RETURN_AND_SET_ERRNO(timerID);
115
116	// init the object members
117	timer->SetTo(timerID, -1);
118
119	*_timer = timerDeleter.Detach();
120	return 0;
121}
122
123
124int
125timer_delete(timer_t timer)
126{
127	status_t error = _kern_delete_timer(timer->id, timer->thread);
128	if (error != B_OK)
129		RETURN_AND_SET_ERRNO(error);
130
131	delete timer;
132	return 0;
133}
134
135
136int
137timer_gettime(timer_t timer, struct itimerspec* value)
138{
139	user_timer_info info;
140	status_t error = _kern_get_timer(timer->id, timer->thread, &info);
141	if (error != B_OK)
142		RETURN_AND_SET_ERRNO(error);
143
144	info_to_itimerspec(info, *value);
145
146	return 0;
147}
148
149
150int
151timer_settime(timer_t timer, int flags, const struct itimerspec* value,
152	struct itimerspec* oldValue)
153{
154	// translate new value
155	bigtime_t nextTime;
156	bigtime_t interval;
157	if (!itimerspec_to_bigtimes(*value, nextTime, interval))
158		RETURN_AND_SET_ERRNO(EINVAL);
159
160	uint32 timeoutFlags = (flags & TIMER_ABSTIME) != 0
161		? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT;
162
163	// set the timer
164	user_timer_info oldInfo;
165	status_t error = _kern_set_timer(timer->id, timer->thread, nextTime,
166		interval, timeoutFlags, oldValue != NULL ? &oldInfo : NULL);
167	if (error != B_OK)
168		RETURN_AND_SET_ERRNO(error);
169
170	// translate old info back
171	if (oldValue != NULL)
172		info_to_itimerspec(oldInfo, *oldValue);
173
174	return 0;
175}
176
177
178int
179timer_getoverrun(timer_t timer)
180{
181	user_timer_info info;
182	status_t error = _kern_get_timer(timer->id, timer->thread, &info);
183	if (error != B_OK)
184		RETURN_AND_SET_ERRNO(error);
185
186	return info.overrun_count;
187}
188