xref: /haiku/src/system/libroot/posix/time/timer_support.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
27 static void
28 info_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 
42 static bool
43 itimerspec_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 
58 static status_t
59 timer_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 
86 int
87 timer_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 
124 int
125 timer_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 
136 int
137 timer_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 
150 int
151 timer_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 
178 int
179 timer_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