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