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/>.
30 struct list wait_entry
;
33 void (*function
)(void *);
37 static struct haiku_thread thread_1
;
39 static tls_decl(struct haiku_thread
*, current_tcb
);
41 unsigned thread_concurrency(void)
44 EINTR_LOOP(ir
, sysconf(_SC_NPROCESSORS_ONLN
));
47 warning("sysconf(_SC_NPROCESSORS_ONLN) returned invalid value %d", ir
);
52 #define do_mutex_init(m) \
55 r = pthread_mutex_init(m, NULL); \
57 fatal("pthread_mutex_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
60 #define do_mutex_done(m) \
63 r = pthread_mutex_destroy(m); \
65 internal(caller_file_line, "mutex_done: pthread_mutex_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
68 #define do_mutex_lock(m) \
71 r = pthread_mutex_lock(m); \
73 internal(caller_file_line, "mutex_lock: pthread_mutex_lock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
76 #define do_mutex_trylock(m) \
79 r = pthread_mutex_trylock(m); \
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)));\
88 #define do_mutex_unlock(m) \
91 r = pthread_mutex_unlock(m); \
93 internal(caller_file_line, "mutex_unlock: pthread_mutex_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
97 /* Warning - too big values cause overflow in the kernel and races. */
98 #define RWMUTEX_NUMBER 32768
100 #define do_rwmutex_init(m) \
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);\
107 #define do_rwmutex_done(m) \
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);\
114 #define do_rwmutex_lock_read(m) \
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);\
124 #define do_rwmutex_unlock_read(m) \
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);\
131 #define do_rwmutex_lock_write(m) \
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);\
141 #define do_rwmutex_unlock_write(m) \
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);\
149 #define do_cond_init(c) \
151 mutex_init_position(&c->mutex pass_position); \
152 list_init(&c->wait_list); \
155 #define do_cond_done(c) \
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"));\
161 #define do_cond_lock(c) \
163 mutex_lock_position(&c->mutex pass_position); \
166 #define do_cond_unlock(c) \
168 mutex_unlock_position(&c->mutex pass_position); \
171 #define do_cond_unlock_signal(c) \
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; \
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);\
189 #define do_cond_unlock_broadcast(c) \
193 list_take(&list, &c->wait_list); \
194 for (l = list.next; l != &list; l = l->next) \
196 mutex_unlock_position(&c->mutex pass_position); \
197 while (list.next != &list) { \
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);\
207 static bool haiku_cond_wait(cond_t
*c
, bigtime_t timeout argument_position
)
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)) {
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
);
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
);
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
);
247 fatal("acquire_sem_etc failed at %s: %x", position_string(position_arg
), s
);
253 #define do_cond_wait(c) \
255 haiku_cond_wait(c, B_INFINITE_TIMEOUT pass_position); \
258 #define do_cond_wait_us(c, us) \
260 return haiku_cond_wait(c, us pass_position); \
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
);
283 tcb
->function(tcb
->arg
);
284 tls_destructor_call();
288 #define do_thread_spawn(t, function, arg, priority, err) \
292 struct haiku_thread *tcb; \
293 tcb = mem_alloc_mayfail(struct haiku_thread *, sizeof(struct haiku_thread), err);\
294 if (unlikely(!tcb)) \
296 haiku_thread_init(tcb pass_position); \
297 tcb->function = function; \
299 switch (priority) { \
300 case PRIORITY_COMPUTE: \
301 b = B_NORMAL_PRIORITY; break; \
303 b = B_DISPLAY_PRIORITY; break; \
304 case PRIORITY_TIMER: \
305 b = B_URGENT_DISPLAY_PRIORITY; break; \
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);\
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);\
322 #define do_thread_join(t) \
324 struct haiku_thread *tcb = *t; \
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); \
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
);