x86: implement 128-bit popcnt
[ajla.git] / tick.c
blob796ce183852d57e449edd52dc22de69903838f5e
1 /*
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
9 * version.
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/>.
19 #include "ajla.h"
21 #include "thread.h"
23 #include "tick.h"
25 #include <time.h>
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
40 #else
42 #ifdef THREAD_NONE
43 #define use_signal true
44 #else
45 #define use_signal (!dll && !thread_tick)
46 #endif
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
52 #if 1
53 #define SIGNAL SIGALRM
54 #define TIMER ITIMER_REAL
55 #elif 0
56 #define SIGNAL SIGVTALRM
57 #define TIMER ITIMER_VIRTUAL
58 #else
59 #define SIGNAL SIGPROF
60 #define TIMER ITIMER_PROF
61 #endif
63 #include <sys/time.h>
64 #include <signal.h>
66 static void tick_signal_handler(int attr_unused sig)
68 #if 0
69 struct timeval tv;
70 gettimeofday(&tv, NULL);
71 debug("tick: %lu.%06lu %u", tv.tv_sec, tv.tv_usec, load_relaxed(tick_stamp_ptr));
72 #endif
73 increment_stamp();
76 static struct sigaction old_sigaction;
78 #endif
80 #ifndef THREAD_NONE
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,
90 thread_set_id(-2);
91 cond_lock(&tick_cond);
92 while (!tick_end) {
93 /*struct timeval tv;
94 gettimeofday(&tv, NULL);
95 debug("tick: %lu.%06lu %u", tv.tv_sec, tv.tv_usec, tick_stamp);*/
96 if (tick_suspended) {
97 cond_wait(&tick_cond);
98 } else {
99 cond_wait_us(&tick_cond, tick_us);
100 increment_stamp();
103 cond_unlock(&tick_cond);
106 #endif
108 #ifdef USE_TIMER_CREATE
109 static timer_t timer_id;
110 static int using_timer_create;
111 #endif
113 void tick_suspend(void)
115 #ifdef USE_TIMER_CREATE
116 if (use_signal && using_timer_create) {
117 int ir;
118 struct itimerspec its;
119 memset(&its, 0, sizeof its);
120 EINTR_LOOP(ir, timer_settime(timer_id, 0, &its, NULL));
121 return;
123 #endif
124 if (use_signal) {
125 #ifdef THREAD_NONE
126 int ir;
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)) {
133 int er = errno;
134 fatal("setitimer failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
136 #endif
137 return;
139 #ifndef THREAD_NONE
140 if (!use_signal) {
141 cond_lock(&tick_cond);
142 tick_suspended = true;
143 cond_unlock(&tick_cond);
144 return;
146 #endif
149 void tick_resume(void)
151 #ifdef USE_TIMER_CREATE
152 if (use_signal && using_timer_create) {
153 int ir;
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));
159 return;
161 #endif
162 if (use_signal) {
163 #ifdef THREAD_NONE
164 int ir;
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)) {
171 int er = errno;
172 fatal("setitimer failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
174 #endif
175 return;
177 #ifndef THREAD_NONE
178 if (!use_signal) {
179 cond_lock(&tick_cond);
180 tick_suspended = false;
181 cond_unlock_signal(&tick_cond);
182 return;
184 #endif
187 void tick_init(void)
189 #ifdef USEABLE_SIGNAL
190 if (use_signal) {
191 int ir;
192 struct sigaction sa;
193 (void)memset(&sa, 0, sizeof sa);
194 sa.sa_handler = tick_signal_handler;
195 sigemptyset(&sa.sa_mask);
196 #ifdef SA_RESTART
197 sa.sa_flags |= SA_RESTART;
198 #endif
199 EINTR_LOOP(ir, sigaction(SIGNAL, &sa, &old_sigaction));
200 if (unlikely(ir == -1)) {
201 int er = errno;
202 fatal("sigaction failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
204 #ifdef USE_TIMER_CREATE
205 if (1) {
206 struct sigevent sev;
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));
213 #else
214 EINTR_LOOP(ir, timer_create(CLOCK_REALTIME, &sev, &timer_id));
215 #endif
216 if (ir == -1) {
217 #ifdef THREAD_NONE
218 goto try_setitimer;
219 #else
220 goto try_thread;
221 #endif
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));
227 if (ir == -1) {
228 int er = errno;
229 fatal("timer_settime failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
231 using_timer_create = 1;
232 } else
233 #endif
235 struct itimerval itv;
236 goto try_setitimer;
237 try_setitimer:
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)) {
243 int er = errno;
244 fatal("setitimer failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
247 } else
248 #endif
250 #ifdef THREAD_NONE
251 not_reached();
252 #else
253 goto try_thread;
254 try_thread:
255 cond_init(&tick_cond);
256 tick_end = false;
257 tick_suspended = false;
258 thread_spawn(&tick_thread, tick_thread_function, NULL, PRIORITY_TIMER, NULL);
259 #endif
263 void tick_done(void)
265 /*debug("ticks: %u", tick_stamp);*/
266 #ifdef USEABLE_SIGNAL
267 if (use_signal) {
268 int ir;
269 #ifdef USE_TIMER_CREATE
270 if (using_timer_create) {
271 EINTR_LOOP(ir, timer_delete(timer_id));
272 if (ir == -1) {
273 int er = errno;
274 fatal("timer_delete failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
276 } else
277 #endif
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)) {
285 int er = errno;
286 fatal("setitimer failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
289 #ifndef THREAD_NONE
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;
295 #endif
296 EINTR_LOOP(ir, sigaction(SIGNAL, &old_sigaction, NULL));
297 if (unlikely(ir == -1)) {
298 int er = errno;
299 fatal("sigaction failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
301 } else
302 #endif
304 #ifdef THREAD_NONE
305 not_reached();
306 #else
307 cond_lock(&tick_cond);
308 tick_end = true;
309 cond_unlock_broadcast(&tick_cond);
310 thread_join(&tick_thread);
311 cond_done(&tick_cond);
312 #endif