x86-64: remove pushes and pops arounf pointer_dereference and
[ajla.git] / th_haiku.c
blobedbc6a57bb00ed2c646fa87ecfc415e9535cdb8f
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 "obj_reg.h"
22 #include "mem_al.h"
23 #include "os.h"
25 #include "thread.h"
27 #ifdef THREAD_HAIKU
29 struct haiku_thread {
30 struct list wait_entry;
31 sem_id wakeup;
32 thread_id id;
33 void (*function)(void *);
34 void *arg;
37 static struct haiku_thread thread_1;
39 static tls_decl(struct haiku_thread *, current_tcb);
41 unsigned thread_concurrency(void)
43 int ir;
44 EINTR_LOOP(ir, sysconf(_SC_NPROCESSORS_ONLN));
45 if (likely(ir > 0))
46 return ir;
47 warning("sysconf(_SC_NPROCESSORS_ONLN) returned invalid value %d", ir);
48 return 1;
52 #define do_mutex_init(m) \
53 do { \
54 int r; \
55 r = pthread_mutex_init(m, NULL); \
56 if (unlikely(r)) \
57 fatal("pthread_mutex_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
58 } while (0)
60 #define do_mutex_done(m) \
61 do { \
62 int r; \
63 r = pthread_mutex_destroy(m); \
64 if (unlikely(r)) \
65 internal(caller_file_line, "mutex_done: pthread_mutex_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
66 } while (0)
68 #define do_mutex_lock(m) \
69 do { \
70 int r; \
71 r = pthread_mutex_lock(m); \
72 if (unlikely(r)) \
73 internal(caller_file_line, "mutex_lock: pthread_mutex_lock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
74 } while (0)
76 #define do_mutex_trylock(m) \
77 do { \
78 int r; \
79 r = pthread_mutex_trylock(m); \
80 if (unlikely(r)) { \
81 if (unlikely(r != EBUSY) && unlikely(r != EDEADLK)) \
82 internal(caller_file_line, "mutex_trylock: pthread_mutex_trylock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
83 return false; \
84 } \
85 return true; \
86 } while (0)
88 #define do_mutex_unlock(m) \
89 do { \
90 int r; \
91 r = pthread_mutex_unlock(m); \
92 if (unlikely(r)) \
93 internal(caller_file_line, "mutex_unlock: pthread_mutex_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
94 } while (0)
97 /* Warning - too big values cause overflow in the kernel and races. */
98 #define RWMUTEX_NUMBER 32768
100 #define do_rwmutex_init(m) \
101 do { \
102 *m = create_sem(RWMUTEX_NUMBER, NULL); \
103 if (unlikely(*m < 0)) \
104 fatal("create_sem failed at %s: %x", position_string(position_arg), *m);\
105 } while (0)
107 #define do_rwmutex_done(m) \
108 do { \
109 status_t s = delete_sem(*m); \
110 if (unlikely(s != B_NO_ERROR)) \
111 fatal("delete_sem failed at %s: %x", position_string(position_arg), s);\
112 } while (0)
114 #define do_rwmutex_lock_read(m) \
115 do { \
116 status_t s; \
117 do { \
118 s = acquire_sem(*m); \
119 } while (unlikely(s == B_INTERRUPTED)); \
120 if (unlikely(s != B_NO_ERROR)) \
121 fatal("acquire_sem failed at %s: %x", position_string(position_arg), s);\
122 } while (0)
124 #define do_rwmutex_unlock_read(m) \
125 do { \
126 status_t s = release_sem(*m); \
127 if (unlikely(s != B_NO_ERROR)) \
128 fatal("release_sem failed at %s: %x", position_string(position_arg), s);\
129 } while (0)
131 #define do_rwmutex_lock_write(m) \
132 do { \
133 status_t s; \
134 do { \
135 s = acquire_sem_etc(*m, RWMUTEX_NUMBER, B_RELATIVE_TIMEOUT, B_INFINITE_TIMEOUT);\
136 } while (unlikely(s == B_INTERRUPTED)); \
137 if (unlikely(s != B_NO_ERROR)) \
138 fatal("acquire_sem failed at %s: %x", position_string(position_arg), s);\
139 } while (0)
141 #define do_rwmutex_unlock_write(m) \
142 do { \
143 status_t s = release_sem_etc(*m, RWMUTEX_NUMBER, 0); \
144 if (unlikely(s != B_NO_ERROR)) \
145 fatal("release_sem failed at %s: %x", position_string(position_arg), s);\
146 } while (0)
149 #define do_cond_init(c) \
150 do { \
151 mutex_init_position(&c->mutex pass_position); \
152 list_init(&c->wait_list); \
153 } while (0)
155 #define do_cond_done(c) \
156 do { \
157 mutex_done_position(&c->mutex pass_position); \
158 ajla_assert_lo(list_is_empty(&c->wait_list), (caller_file_line, "cond_done: wait list is not empty"));\
159 } while (0)
161 #define do_cond_lock(c) \
162 do { \
163 mutex_lock_position(&c->mutex pass_position); \
164 } while (0)
166 #define do_cond_unlock(c) \
167 do { \
168 mutex_unlock_position(&c->mutex pass_position); \
169 } while (0)
171 #define do_cond_unlock_signal(c) \
172 do { \
173 struct haiku_thread *tcb; \
174 if (unlikely(!list_is_empty(&c->wait_list))) { \
175 tcb = get_struct(c->wait_list.next, struct haiku_thread, wait_entry);\
176 list_del(&tcb->wait_entry); \
177 tcb->wait_entry.prev = NULL; \
178 } else { \
179 tcb = NULL; \
181 mutex_unlock_position(&c->mutex pass_position); \
182 if (unlikely(tcb != NULL)) { \
183 status_t s = release_sem(tcb->wakeup); \
184 if (unlikely(s != B_NO_ERROR)) \
185 fatal("release_sem failed at %s: %x", position_string(position_arg), s);\
187 } while (0)
189 #define do_cond_unlock_broadcast(c) \
190 do { \
191 struct list list; \
192 struct list *l; \
193 list_take(&list, &c->wait_list); \
194 for (l = list.next; l != &list; l = l->next) \
195 l->prev = NULL; \
196 mutex_unlock_position(&c->mutex pass_position); \
197 while (list.next != &list) { \
198 status_t s; \
199 struct haiku_thread *tcb = get_struct(list.next, struct haiku_thread, wait_entry);\
200 list.next = tcb->wait_entry.next; \
201 s = release_sem(tcb->wakeup); \
202 if (unlikely(s != B_NO_ERROR)) \
203 fatal("release_sem failed at %s: %x", position_string(position_arg), s);\
205 } while (0)
207 static bool haiku_cond_wait(cond_t *c, bigtime_t timeout argument_position)
209 int32 count;
210 status_t s;
211 struct haiku_thread *tcb = tls_get(struct haiku_thread *, current_tcb);
213 s = get_sem_count(tcb->wakeup, &count);
214 if (unlikely(s != B_NO_ERROR))
215 fatal("get_sem_count failed at %s: %x", position_string(position_arg), s);
217 if (unlikely(count > 0)) {
218 do {
219 s = acquire_sem_etc(tcb->wakeup, count, B_RELATIVE_TIMEOUT, B_INFINITE_TIMEOUT);
220 } while (unlikely(s == B_INTERRUPTED));
221 if (unlikely(s != B_NO_ERROR))
222 fatal("acquire_sem_etc failed at %s: %x", position_string(position_arg), s);
225 list_add(&c->wait_list, &tcb->wait_entry);
226 mutex_unlock_position(&c->mutex pass_position);
228 do {
229 s = acquire_sem_etc(tcb->wakeup, 1, B_RELATIVE_TIMEOUT, timeout);
230 } while (unlikely(s == B_INTERRUPTED));
232 mutex_lock_position(&c->mutex pass_position);
234 if (unlikely(s != B_NO_ERROR)) {
235 if (s == B_TIMED_OUT || s == B_WOULD_BLOCK) {
236 if (likely(tcb->wait_entry.prev != NULL)) {
237 list_del(&tcb->wait_entry);
238 return false;
240 do {
241 s = acquire_sem(tcb->wakeup);
242 } while (unlikely(s == B_INTERRUPTED));
243 if (unlikely(s != B_NO_ERROR))
244 fatal("acquire_sem failed at %s: %x", position_string(position_arg), s);
245 return true;
247 fatal("acquire_sem_etc failed at %s: %x", position_string(position_arg), s);
250 return true;
253 #define do_cond_wait(c) \
254 do { \
255 haiku_cond_wait(c, B_INFINITE_TIMEOUT pass_position); \
256 } while (0)
258 #define do_cond_wait_us(c, us) \
259 do { \
260 return haiku_cond_wait(c, us pass_position); \
261 } while (0)
264 static void haiku_thread_init(struct haiku_thread *tcb argument_position)
266 tcb->wakeup = create_sem(0, NULL);
267 if (unlikely(tcb->wakeup < 0))
268 fatal("create_sem failed at %s: %x", caller_file_line, tcb->wakeup);
271 static void haiku_thread_done(struct haiku_thread *tcb argument_position)
273 status_t s = delete_sem(tcb->wakeup);
274 if (unlikely(s != B_NO_ERROR))
275 fatal("delete_sem failed at %s: %x", caller_file_line, tcb->wakeup);
278 static int32 haiku_thread_function(void *tcb_)
280 struct haiku_thread *tcb = cast_cpp(struct haiku_thread *, tcb_);
281 tls_set(struct haiku_thread *, current_tcb, tcb);
282 asm_setup_thread();
283 tcb->function(tcb->arg);
284 tls_destructor_call();
285 return 0;
288 #define do_thread_spawn(t, function, arg, priority, err) \
289 do { \
290 status_t s; \
291 int32 b; \
292 struct haiku_thread *tcb; \
293 tcb = mem_alloc_mayfail(struct haiku_thread *, sizeof(struct haiku_thread), err);\
294 if (unlikely(!tcb)) \
295 return false; \
296 haiku_thread_init(tcb pass_position); \
297 tcb->function = function; \
298 tcb->arg = arg; \
299 switch (priority) { \
300 case PRIORITY_COMPUTE: \
301 b = B_NORMAL_PRIORITY; break; \
302 case PRIORITY_IO: \
303 b = B_DISPLAY_PRIORITY; break; \
304 case PRIORITY_TIMER: \
305 b = B_URGENT_DISPLAY_PRIORITY; break; \
306 default: \
307 b = B_NORMAL_PRIORITY; break; \
309 tcb->id = spawn_thread(haiku_thread_function, NULL, b, tcb); \
310 if (unlikely(tcb->id < 0)) { \
311 ajla_error_t e = error_from_errno(EC_SYSCALL, tcb->id); \
312 fatal_mayfail(e, err, "spawn_thread failed at %s: %x", position_string(position_arg), tcb->id);\
313 mem_free(tcb); \
314 return false; \
316 s = resume_thread(tcb->id); \
317 if (unlikely(s != B_NO_ERROR)) \
318 fatal("resume_thread failed at %s: %x", caller_file_line, s);\
319 *t = tcb; \
320 } while (0)
322 #define do_thread_join(t) \
323 do { \
324 struct haiku_thread *tcb = *t; \
325 status_t s, r; \
326 do { \
327 s = wait_for_thread(tcb->id, &r); \
328 } while (unlikely(s == B_INTERRUPTED)); \
329 if (unlikely(s != B_NO_ERROR)) \
330 fatal("wait_for_thread failed at %s: %x", caller_file_line, s);\
331 haiku_thread_done(tcb pass_position); \
332 mem_free(tcb); \
333 } while (0)
336 #include "th_com.inc"
339 void thread_init(void)
341 haiku_thread_init(&thread_1 pass_file_line);
342 tls_init(struct haiku_thread *, current_tcb);
343 tls_set(struct haiku_thread *, current_tcb, &thread_1);
344 thread_common_init();
347 void thread_done(void)
349 thread_common_done();
350 haiku_thread_done(&thread_1 pass_file_line);
351 tls_done(struct haiku_thread *, current_tcb);
354 #endif