1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <runtime/thread.h>
6
7 #include <zircon/stack.h>
8 #include <zircon/syscalls.h>
9 #include <stddef.h>
10 #include <stdint.h>
11
12 // An zxr_thread_t starts its life JOINABLE.
13 // - If someone calls zxr_thread_join on it, it transitions to JOINED.
14 // - If someone calls zxr_thread_detach on it, it transitions to DETACHED.
15 // - When it begins exiting, the EXITING state is entered.
16 // - When it is no longer using its memory and handle resources, it transitions
17 // to DONE. If the thread was DETACHED prior to EXITING, this transition MAY
18 // not happen.
19 // No other transitions occur.
20
21 enum {
22 JOINABLE,
23 DETACHED,
24 JOINED,
25 EXITING,
26 DONE,
27 };
28
29 typedef struct {
30 zxr_thread_entry_t entry;
31 zx_handle_t handle;
32 atomic_int state;
33 } zxr_internal_thread_t;
34
35 // zxr_thread_t should reserve enough size for our internal data.
36 _Static_assert(sizeof(zxr_thread_t) == sizeof(zxr_internal_thread_t),
37 "Update zxr_thread_t size for this platform.");
38
to_internal(zxr_thread_t * external)39 static inline zxr_internal_thread_t* to_internal(zxr_thread_t* external) {
40 return (zxr_internal_thread_t*)(external);
41 }
42
zxr_thread_destroy(zxr_thread_t * thread)43 zx_status_t zxr_thread_destroy(zxr_thread_t* thread) {
44 zx_handle_t handle = to_internal(thread)->handle;
45 to_internal(thread)->handle = ZX_HANDLE_INVALID;
46 return handle == ZX_HANDLE_INVALID ? ZX_OK : _zx_handle_close(handle);
47 }
48
49 // Put the thread into EXITING state. Returns the previous state.
begin_exit(zxr_internal_thread_t * thread)50 static int begin_exit(zxr_internal_thread_t* thread) {
51 return atomic_exchange_explicit(&thread->state, EXITING, memory_order_release);
52 }
53
54 // Claim the thread as JOINED or DETACHED. Returns true on success, which only
55 // happens if the previous state was JOINABLE. Always returns the previous state.
claim_thread(zxr_internal_thread_t * thread,int new_state,int * old_state)56 static bool claim_thread(zxr_internal_thread_t* thread, int new_state, int* old_state) {
57 *old_state = JOINABLE;
58 return atomic_compare_exchange_strong_explicit(
59 &thread->state, old_state, new_state,
60 memory_order_acq_rel, memory_order_acquire);
61 }
62
63 // Extract the handle from the thread structure. This must only be called by the thread
64 // itself (i.e., this is not thread-safe).
take_handle(zxr_internal_thread_t * thread)65 static zx_handle_t take_handle(zxr_internal_thread_t* thread) {
66 zx_handle_t tmp = thread->handle;
67 thread->handle = ZX_HANDLE_INVALID;
68 return tmp;
69 }
70
exit_non_detached(zxr_internal_thread_t * thread)71 static _Noreturn void exit_non_detached(zxr_internal_thread_t* thread) {
72 // As soon as thread->state has changed to to DONE, a caller of zxr_thread_join
73 // might complete and deallocate the memory containing the thread descriptor.
74 // Hence it's no longer safe to touch *thread or read anything out of it.
75 // Therefore we must extract the thread handle before that transition
76 // happens.
77 zx_handle_t handle = take_handle(thread);
78
79 // Wake the _zx_futex_wait in zxr_thread_join (below), and then die.
80 // This has to be done with the special four-in-one vDSO call because
81 // as soon as the state transitions to DONE, the joiner is free to unmap
82 // our stack out from under us. Note there is a benign race here still: if
83 // the address is unmapped and our futex_wake fails, it's OK; if the memory
84 // is reused for something else and our futex_wake tickles somebody
85 // completely unrelated, well, that's why futex_wait can always have
86 // spurious wakeups.
87 _zx_futex_wake_handle_close_thread_exit(&thread->state, 1, DONE, handle);
88 __builtin_trap();
89 }
90
thread_trampoline(uintptr_t ctx,uintptr_t arg)91 static _Noreturn void thread_trampoline(uintptr_t ctx, uintptr_t arg) {
92 zxr_internal_thread_t* thread = (zxr_internal_thread_t*)ctx;
93
94 thread->entry((void*)arg);
95
96 int old_state = begin_exit(thread);
97 switch (old_state) {
98 case JOINABLE:
99 // Nobody's watching right now, but they might start watching as we
100 // exit. Just in case, behave as if we've been joined and wake the
101 // futex on our way out.
102 case JOINED:
103 // Somebody loves us! Or at least intends to inherit when we die.
104 exit_non_detached(thread);
105 break;
106 }
107
108 // Cannot be in DONE, EXITING, or DETACHED and reach here. For DETACHED, it
109 // is the responsibility of a higher layer to ensure this is not reached.
110 __builtin_trap();
111 }
112
zxr_thread_exit_unmap_if_detached(zxr_thread_t * thread,zx_handle_t vmar,uintptr_t addr,size_t len)113 _Noreturn void zxr_thread_exit_unmap_if_detached(
114 zxr_thread_t* thread, zx_handle_t vmar, uintptr_t addr, size_t len) {
115
116 int old_state = begin_exit(to_internal(thread));
117 switch (old_state) {
118 case DETACHED: {
119 zx_handle_t handle = take_handle(to_internal(thread));
120 _zx_vmar_unmap_handle_close_thread_exit(vmar, addr, len, handle);
121 break;
122 }
123 // See comments in thread_trampoline.
124 case JOINABLE:
125 case JOINED:
126 exit_non_detached(to_internal(thread));
127 break;
128 }
129
130 // Cannot be in DONE or the EXITING and reach here.
131 __builtin_trap();
132 }
133
134 // Local implementation so libruntime does not depend on libc.
local_strlen(const char * s)135 static size_t local_strlen(const char* s) {
136 size_t len = 0;
137 while (*s++ != '\0')
138 ++len;
139 return len;
140 }
141
initialize_thread(zxr_internal_thread_t * thread,zx_handle_t handle,bool detached)142 static void initialize_thread(zxr_internal_thread_t* thread,
143 zx_handle_t handle, bool detached) {
144 *thread = (zxr_internal_thread_t){ .handle = handle, };
145 atomic_init(&thread->state, detached ? DETACHED : JOINABLE);
146 }
147
zxr_thread_create(zx_handle_t process,const char * name,bool detached,zxr_thread_t * thread)148 zx_status_t zxr_thread_create(zx_handle_t process, const char* name,
149 bool detached, zxr_thread_t* thread) {
150 initialize_thread(to_internal(thread), ZX_HANDLE_INVALID, detached);
151 if (name == NULL)
152 name = "";
153 size_t name_length = local_strlen(name) + 1;
154 return _zx_thread_create(process, name, name_length, 0, &to_internal(thread)->handle);
155 }
156
zxr_thread_start(zxr_thread_t * thread,uintptr_t stack_addr,size_t stack_size,zxr_thread_entry_t entry,void * arg)157 zx_status_t zxr_thread_start(zxr_thread_t* thread, uintptr_t stack_addr, size_t stack_size, zxr_thread_entry_t entry, void* arg) {
158 to_internal(thread)->entry = entry;
159
160 // compute the starting address of the stack
161 uintptr_t sp = compute_initial_stack_pointer(stack_addr, stack_size);
162
163 // kick off the new thread
164 zx_status_t status = _zx_thread_start(to_internal(thread)->handle,
165 (uintptr_t)thread_trampoline, sp,
166 (uintptr_t)thread, (uintptr_t)arg);
167
168 if (status != ZX_OK)
169 zxr_thread_destroy(thread);
170 return status;
171 }
172
wait_for_done(zxr_internal_thread_t * thread,int32_t old_state)173 static void wait_for_done(zxr_internal_thread_t* thread, int32_t old_state) {
174 do {
175 switch (_zx_futex_wait(&thread->state, old_state, ZX_HANDLE_INVALID, ZX_TIME_INFINITE)) {
176 case ZX_ERR_BAD_STATE: // Never blocked because it had changed.
177 case ZX_OK: // Woke up because it might have changed.
178 old_state = atomic_load_explicit(&thread->state,
179 memory_order_acquire);
180 break;
181 default:
182 __builtin_trap();
183 }
184 // Wait until we reach the DONE state, even if we observe the
185 // intermediate EXITING state.
186 } while (old_state == JOINED || old_state == EXITING);
187
188 if (old_state != DONE)
189 __builtin_trap();
190 }
191
zxr_thread_join(zxr_thread_t * external_thread)192 zx_status_t zxr_thread_join(zxr_thread_t* external_thread) {
193 zxr_internal_thread_t* thread = to_internal(external_thread);
194
195 int old_state;
196 // Try to claim the join slot on this thread.
197 if (claim_thread(thread, JOINED, &old_state)) {
198 wait_for_done(thread, JOINED);
199 } else {
200 switch (old_state) {
201 case JOINED:
202 case DETACHED:
203 return ZX_ERR_INVALID_ARGS;
204 case EXITING:
205 // Since it is undefined to call zxr_thread_join on a thread
206 // that has already been detached or joined, we assume the state
207 // prior to EXITING was JOINABLE, and act as if we had
208 // successfully transitioned to JOINED.
209 wait_for_done(thread, EXITING);
210 // Fall-through to DONE case
211 case DONE:
212 break;
213 default:
214 __builtin_trap();
215 }
216 }
217
218 // The thread has already closed its own handle.
219 return ZX_OK;
220 }
221
zxr_thread_detach(zxr_thread_t * thread)222 zx_status_t zxr_thread_detach(zxr_thread_t* thread) {
223 int old_state;
224 // Try to claim the join slot on this thread on behalf of the thread.
225 if (!claim_thread(to_internal(thread), DETACHED, &old_state)) {
226 switch (old_state) {
227 case DETACHED:
228 case JOINED:
229 return ZX_ERR_INVALID_ARGS;
230 case EXITING: {
231 // Since it is undefined behavior to call zxr_thread_detach on a
232 // thread that has already been detached or joined, we assume
233 // the state prior to EXITING was JOINABLE. However, since the
234 // thread is already shutting down, it is too late to tell it to
235 // clean itself up. Since the thread is still running, we cannot
236 // just return ZX_ERR_BAD_STATE, which would suggest we couldn't detach and
237 // the thread has already finished running. Instead, we call join,
238 // which will return soon due to the thread being actively shutting down,
239 // and then return ZX_ERR_BAD_STATE to tell the caller that they
240 // must manually perform any post-join work.
241 zx_status_t ret = zxr_thread_join(thread);
242 if (unlikely(ret != ZX_OK)) {
243 if (unlikely(ret != ZX_ERR_INVALID_ARGS)) {
244 __builtin_trap();
245 }
246 return ret;
247 }
248 // Fall-through to DONE case.
249 __FALLTHROUGH;
250 }
251 case DONE:
252 return ZX_ERR_BAD_STATE;
253 default:
254 __builtin_trap();
255 }
256 }
257
258 return ZX_OK;
259 }
260
zxr_thread_detached(zxr_thread_t * thread)261 bool zxr_thread_detached(zxr_thread_t* thread) {
262 int state = atomic_load_explicit(&to_internal(thread)->state, memory_order_acquire);
263 return state == DETACHED;
264 }
265
zxr_thread_get_handle(zxr_thread_t * thread)266 zx_handle_t zxr_thread_get_handle(zxr_thread_t* thread) {
267 return to_internal(thread)->handle;
268 }
269
zxr_thread_adopt(zx_handle_t handle,zxr_thread_t * thread)270 zx_status_t zxr_thread_adopt(zx_handle_t handle, zxr_thread_t* thread) {
271 initialize_thread(to_internal(thread), handle, false);
272 return handle == ZX_HANDLE_INVALID ? ZX_ERR_BAD_HANDLE : ZX_OK;
273 }
274