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