2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
27 uint32_t tick_us
= DEFAULT_TICK_US
;
28 uint32_t thread_tick
= 0;
29 atomic_type tick_stamp_t
*tick_stamp_ptr
;
31 static attr_always_inline
void increment_stamp(void)
33 store_relaxed(tick_stamp_ptr
, load_relaxed(tick_stamp_ptr
) + 1);
36 #ifndef USEABLE_SIGNAL
38 #define use_signal false
43 #define use_signal true
45 #define use_signal (!dll && !thread_tick)
49 * SIGVTALRM and SIGPROF is unuseable due to a bug:
50 * it doesn't count if the main thread is idle, even though other threads are active
53 #define SIGNAL SIGALRM
54 #define TIMER ITIMER_REAL
56 #define SIGNAL SIGVTALRM
57 #define TIMER ITIMER_VIRTUAL
59 #define SIGNAL SIGPROF
60 #define TIMER ITIMER_PROF
66 static void tick_signal_handler(int attr_unused sig
)
70 gettimeofday(&tv
, NULL
);
71 debug("tick: %lu.%06lu %u", tv
.tv_sec
, tv
.tv_usec
, load_relaxed(tick_stamp_ptr
));
76 static struct sigaction old_sigaction
;
82 static thread_t tick_thread
;
83 static cond_t tick_cond
;
84 static uchar_efficient_t tick_end
;
85 static uchar_efficient_t tick_suspended
;
87 /*#include <sys/time.h>*/
89 thread_function_decl(tick_thread_function
,
91 cond_lock(&tick_cond
);
94 gettimeofday(&tv, NULL);
95 debug("tick: %lu.%06lu %u", tv.tv_sec, tv.tv_usec, tick_stamp);*/
97 cond_wait(&tick_cond
);
99 cond_wait_us(&tick_cond
, tick_us
);
103 cond_unlock(&tick_cond
);
108 #ifdef USE_TIMER_CREATE
109 static timer_t timer_id
;
110 static int using_timer_create
;
113 void tick_suspend(void)
115 #ifdef USE_TIMER_CREATE
116 if (use_signal
&& using_timer_create
) {
118 struct itimerspec its
;
119 memset(&its
, 0, sizeof its
);
120 EINTR_LOOP(ir
, timer_settime(timer_id
, 0, &its
, NULL
));
127 struct itimerval itv
;
128 itv
.it_interval
.tv_sec
= 0;
129 itv
.it_interval
.tv_usec
= 0;
130 itv
.it_value
= itv
.it_interval
;
131 EINTR_LOOP(ir
, setitimer(TIMER
, &itv
, NULL
));
132 if (unlikely(ir
== -1)) {
134 fatal("setitimer failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
141 cond_lock(&tick_cond
);
142 tick_suspended
= true;
143 cond_unlock(&tick_cond
);
149 void tick_resume(void)
151 #ifdef USE_TIMER_CREATE
152 if (use_signal
&& using_timer_create
) {
154 struct itimerspec its
;
155 its
.it_interval
.tv_sec
= tick_us
/ 1000000;
156 its
.it_interval
.tv_nsec
= tick_us
% 1000000 * 1000;
157 its
.it_value
= its
.it_interval
;
158 EINTR_LOOP(ir
, timer_settime(timer_id
, 0, &its
, NULL
));
165 struct itimerval itv
;
166 itv
.it_interval
.tv_sec
= tick_us
/ 1000000;
167 itv
.it_interval
.tv_usec
= tick_us
% 1000000;
168 itv
.it_value
= itv
.it_interval
;
169 EINTR_LOOP(ir
, setitimer(TIMER
, &itv
, NULL
));
170 if (unlikely(ir
== -1)) {
172 fatal("setitimer failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
179 cond_lock(&tick_cond
);
180 tick_suspended
= false;
181 cond_unlock_signal(&tick_cond
);
189 #ifdef USEABLE_SIGNAL
193 (void)memset(&sa
, 0, sizeof sa
);
194 sa
.sa_handler
= tick_signal_handler
;
195 sigemptyset(&sa
.sa_mask
);
197 sa
.sa_flags
|= SA_RESTART
;
199 EINTR_LOOP(ir
, sigaction(SIGNAL
, &sa
, &old_sigaction
));
200 if (unlikely(ir
== -1)) {
202 fatal("sigaction failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
204 #ifdef USE_TIMER_CREATE
207 struct itimerspec its
;
208 memset(&sev
, 0, sizeof sev
);
209 sev
.sigev_notify
= SIGEV_SIGNAL
;
210 sev
.sigev_signo
= SIGNAL
;
211 #ifdef HAVE_CLOCK_MONOTONIC
212 EINTR_LOOP(ir
, timer_create(CLOCK_MONOTONIC
, &sev
, &timer_id
));
214 EINTR_LOOP(ir
, timer_create(CLOCK_REALTIME
, &sev
, &timer_id
));
223 its
.it_interval
.tv_sec
= tick_us
/ 1000000;
224 its
.it_interval
.tv_nsec
= tick_us
% 1000000 * 1000;
225 its
.it_value
= its
.it_interval
;
226 EINTR_LOOP(ir
, timer_settime(timer_id
, 0, &its
, NULL
));
229 fatal("timer_settime failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
231 using_timer_create
= 1;
235 struct itimerval itv
;
238 itv
.it_interval
.tv_sec
= tick_us
/ 1000000;
239 itv
.it_interval
.tv_usec
= tick_us
% 1000000;
240 itv
.it_value
= itv
.it_interval
;
241 EINTR_LOOP(ir
, setitimer(TIMER
, &itv
, NULL
));
242 if (unlikely(ir
== -1)) {
244 fatal("setitimer failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
255 cond_init(&tick_cond
);
257 tick_suspended
= false;
258 thread_spawn(&tick_thread
, tick_thread_function
, NULL
, PRIORITY_TIMER
, NULL
);
265 /*debug("ticks: %u", tick_stamp);*/
266 #ifdef USEABLE_SIGNAL
269 #ifdef USE_TIMER_CREATE
270 if (using_timer_create
) {
271 EINTR_LOOP(ir
, timer_delete(timer_id
));
274 fatal("timer_delete failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
279 struct itimerval itv
;
280 itv
.it_interval
.tv_sec
= 0;
281 itv
.it_interval
.tv_usec
= 0;
282 itv
.it_value
= itv
.it_interval
;
283 EINTR_LOOP(ir
, setitimer(TIMER
, &itv
, NULL
));
284 if (unlikely(ir
== -1)) {
286 fatal("setitimer failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
291 * If we have threads, the signal handler may be queued for another
292 * theread. So, we must set it to ignore to avoid damage.
294 old_sigaction
.sa_handler
= SIG_IGN
;
296 EINTR_LOOP(ir
, sigaction(SIGNAL
, &old_sigaction
, NULL
));
297 if (unlikely(ir
== -1)) {
299 fatal("sigaction failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
307 cond_lock(&tick_cond
);
309 cond_unlock_broadcast(&tick_cond
);
310 thread_join(&tick_thread
);
311 cond_done(&tick_cond
);