x86-64: hack the ABI of cg_upcall_ipret_copy_variable_to_pointer
[ajla.git] / timer.c
blob5b71e28094bcf8ec8928a9eaadedb39fa019540f
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 "mem_al.h"
22 #include "addrlock.h"
23 #include "tree.h"
24 #include "list.h"
25 #include "iomux.h"
27 #include "timer.h"
29 struct timer {
30 struct tree_entry entry;
31 ajla_time_t t;
32 struct list wait_list;
35 static struct tree timer_tree;
36 static mutex_t timer_tree_mutex;
38 #ifdef TIMER_THREAD
39 static thread_t timer_thread;
40 static cond_t timer_cond;
41 static uchar_efficient_t timer_thread_exit;
42 #endif
44 static inline void timer_lock(void)
46 mutex_lock(&timer_tree_mutex);
49 static inline void timer_unlock(void)
51 mutex_unlock(&timer_tree_mutex);
54 static bool timer_first(ajla_time_t *f)
56 struct tree_entry *e;
57 struct timer *t;
59 e = tree_first(&timer_tree);
60 again:
61 if (!e)
62 return false;
63 t = get_struct(e, struct timer, entry);
64 if (list_is_empty(&t->wait_list)) {
65 e = tree_next(&t->entry);
66 tree_delete(&t->entry);
67 mem_free(t);
68 goto again;
71 *f = t->t;
72 return true;
75 #ifdef TIMER_THREAD
76 static
77 #endif
78 uint32_t timer_wait_now(void)
80 uint32_t u = IOMUX_INDEFINITE_WAIT;
81 ajla_time_t mt;
82 timer_lock();
83 if (timer_first(&mt)) {
84 ajla_time_t m = os_time_monotonic();
85 if (m >= mt) {
86 u = 0;
87 } else if (unlikely(mt - m >= IOMUX_INDEFINITE_WAIT - 1)) {
88 u = IOMUX_INDEFINITE_WAIT - 1;
89 } else {
90 u = mt - m;
93 timer_unlock();
94 return u;
97 static int timer_compare(const struct tree_entry *e, uintptr_t mtp)
99 struct timer *t = get_struct(e, struct timer, entry);
100 ajla_time_t mt = *cast_ptr(ajla_time_t *, num_to_ptr(mtp));
101 if (t->t < mt)
102 return -1;
103 if (t->t > mt)
104 return 1;
105 return 0;
108 bool timer_register_wait(ajla_time_t mt, mutex_t **mutex_to_lock, struct list *list_entry, ajla_error_t *err)
110 struct tree_insert_position ins;
111 struct timer *t;
112 struct tree_entry *e;
114 timer_lock();
116 e = tree_find_for_insert(&timer_tree, timer_compare, ptr_to_num(&mt), &ins);
117 if (unlikely(e != NULL)) {
118 t = get_struct(e, struct timer, entry);
119 } else {
120 t = mem_alloc_mayfail(struct timer *, sizeof(struct timer), err);
121 if (unlikely(!t)) {
122 timer_unlock();
123 return false;
125 t->t = mt;
126 list_init(&t->wait_list);
127 tree_insert_after_find(&t->entry, &ins);
130 *mutex_to_lock = &timer_tree_mutex;
131 list_add(&t->wait_list, list_entry);
133 #ifndef THREAD_NONE
134 if (tree_first(&timer_tree) == &t->entry) {
135 #ifndef TIMER_THREAD
136 os_notify();
137 #else
138 timer_unlock();
139 cond_lock(&timer_cond);
140 cond_unlock_signal(&timer_cond);
141 return true;
142 #endif
144 #endif
146 timer_unlock();
147 return true;
150 #ifdef TIMER_THREAD
151 static
152 #endif
153 void timer_check_all(void)
155 struct tree_entry *e;
156 again:
157 timer_lock();
158 e = tree_first(&timer_tree);
159 if (unlikely(e != NULL)) {
160 ajla_time_t m = os_time_monotonic();
161 struct timer *t;
162 t = get_struct(e, struct timer, entry);
163 if (t->t <= m) {
164 tree_delete(&t->entry);
165 call(wake_up_wait_list)(&t->wait_list, &timer_tree_mutex, true);
166 mem_free(t);
167 goto again;
170 timer_unlock();
173 #ifdef TIMER_THREAD
174 thread_function_decl(timer_thread_fn,
175 cond_lock(&timer_cond);
176 while (likely(!timer_thread_exit)) {
177 uint32_t us;
178 us = timer_wait_now();
179 if (us == IOMUX_INDEFINITE_WAIT)
180 cond_wait(&timer_cond);
181 else
182 cond_wait_us(&timer_cond, us);
183 timer_check_all();
185 cond_unlock(&timer_cond);
187 #endif
189 void timer_init(void)
191 tree_init(&timer_tree);
192 mutex_init(&timer_tree_mutex);
193 #ifdef TIMER_THREAD
194 cond_init(&timer_cond);
195 timer_thread_exit = 0;
196 thread_spawn(&timer_thread, timer_thread_fn, NULL, PRIORITY_TIMER, NULL);
197 #endif
200 void timer_done(void)
202 #ifdef TIMER_THREAD
203 cond_lock(&timer_cond);
204 timer_thread_exit = 1;
205 cond_unlock_signal(&timer_cond);
206 thread_join(&timer_thread);
207 cond_done(&timer_cond);
208 #endif
209 while (unlikely(!tree_is_empty(&timer_tree))) {
210 struct timer *t = get_struct(tree_any(&timer_tree), struct timer, entry);
211 tree_delete(&t->entry);
212 mem_free(t);
214 mutex_done(&timer_tree_mutex);