| 1 | #include <thread/thread.h> |
| 2 | |
| 3 | static void join_check(struct thread *t) { |
| 4 | kassert(t); |
| 5 | kassert(t != thread_get_current(), "thread '%s' joined itself" , t->name); |
| 6 | kassert(irql_get() == IRQL_PASSIVE_LEVEL, "thread_join() blocks" ); |
| 7 | |
| 8 | enum thread_flags f = thread_or_flags(t, flags: THREAD_FLAG_JOINED); |
| 9 | kassert(f & THREAD_FLAG_JOINABLE, "join on a detached thread '%s'" , |
| 10 | t->name); |
| 11 | kassert(!(f & THREAD_FLAG_JOINED), "thread '%s' joined twice" , t->name); |
| 12 | } |
| 13 | |
| 14 | int thread_join(struct thread *t) { |
| 15 | join_check(t); |
| 16 | |
| 17 | enum irql irql = spin_lock(&t->join_lock); |
| 18 | while (thread_get_state(t) != THREAD_STATE_ZOMBIE) |
| 19 | condvar_wait(cv: &t->join_cv, lock: &t->join_lock, irql, out: &irql); |
| 20 | |
| 21 | int status = t->exit_status; |
| 22 | spin_unlock(&t->join_lock, irql); |
| 23 | |
| 24 | thread_put(t); |
| 25 | return status; |
| 26 | } |
| 27 | |
| 28 | bool thread_join_timeout(struct thread *t, time_ms_t timeout_ms, |
| 29 | int *status_out) { |
| 30 | join_check(t); |
| 31 | |
| 32 | enum irql irql = spin_lock(&t->join_lock); |
| 33 | while (thread_get_state(t) != THREAD_STATE_ZOMBIE) { |
| 34 | enum wake_reason r = condvar_wait_timeout(cv: &t->join_cv, lock: &t->join_lock, |
| 35 | timeout_ms, irql, out: &irql); |
| 36 | |
| 37 | if (r == WAKE_REASON_TIMEOUT && |
| 38 | thread_get_state(t) != THREAD_STATE_ZOMBIE) { |
| 39 | spin_unlock(&t->join_lock, irql); |
| 40 | /* caller must retry the join or detach */ |
| 41 | thread_and_flags(t, flags: ~THREAD_FLAG_JOINED); |
| 42 | return false; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | if (status_out) |
| 47 | *status_out = t->exit_status; |
| 48 | |
| 49 | spin_unlock(&t->join_lock, irql); |
| 50 | |
| 51 | thread_put(t); |
| 52 | return true; |
| 53 | } |
| 54 | |
| 55 | void thread_detach(struct thread *t) { |
| 56 | enum thread_flags f = thread_and_flags(t, flags: ~THREAD_FLAG_JOINABLE); |
| 57 | kassert(f & THREAD_FLAG_JOINABLE); |
| 58 | kassert(!(f & THREAD_FLAG_JOINED), "detach races an in-flight join" ); |
| 59 | |
| 60 | thread_put(t); |
| 61 | } |
| 62 | |