1#include <thread/thread.h>
2
3static 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
14int 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
28bool 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
55void 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