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