Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdio.h>
28 : : #include <stdlib.h>
29 : : #include <errno.h>
30 : :
31 : : #include "py/runtime.h"
32 : : #include "py/mpthread.h"
33 : : #include "py/gc.h"
34 : :
35 : : #if MICROPY_PY_THREAD
36 : :
37 : : #include <fcntl.h>
38 : : #include <signal.h>
39 : : #include <sched.h>
40 : : #include <semaphore.h>
41 : :
42 : : #include "shared/runtime/gchelper.h"
43 : :
44 : : // Some platforms don't have SIGRTMIN but if we do have it, use it to avoid
45 : : // potential conflict with other uses of the more commonly used SIGUSR1.
46 : : #ifdef SIGRTMIN
47 : : #define MP_THREAD_GC_SIGNAL (SIGRTMIN + 5)
48 : : #ifdef __ANDROID__
49 : : #define MP_THREAD_TERMINATE_SIGNAL (SIGRTMIN + 6)
50 : : #endif
51 : : #else
52 : : #define MP_THREAD_GC_SIGNAL (SIGUSR1)
53 : : #ifdef __ANDROID__
54 : : #define MP_THREAD_TERMINATE_SIGNAL (SIGUSR2)
55 : : #endif
56 : : #endif
57 : :
58 : : // This value seems to be about right for both 32-bit and 64-bit builds.
59 : : #define THREAD_STACK_OVERFLOW_MARGIN (8192)
60 : :
61 : : // this structure forms a linked list, one node per active thread
62 : : typedef struct _mp_thread_t {
63 : : pthread_t id; // system id of thread
64 : : int ready; // whether the thread is ready and running
65 : : void *arg; // thread Python args, a GC root pointer
66 : : struct _mp_thread_t *next;
67 : : } mp_thread_t;
68 : :
69 : : static pthread_key_t tls_key;
70 : :
71 : : // The mutex is used for any code in this port that needs to be thread safe.
72 : : // Specifically for thread management, access to the linked list is one example.
73 : : // But also, e.g. scheduler state.
74 : : static mp_thread_recursive_mutex_t thread_mutex;
75 : : static mp_thread_t *thread;
76 : :
77 : : // this is used to synchronise the signal handler of the thread
78 : : // it's needed because we can't use any pthread calls in a signal handler
79 : : #if defined(__APPLE__)
80 : : static char thread_signal_done_name[25];
81 : : static sem_t *thread_signal_done_p;
82 : : #else
83 : : static sem_t thread_signal_done;
84 : : #endif
85 : :
86 : 61524 : void mp_thread_unix_begin_atomic_section(void) {
87 : 61524 : mp_thread_recursive_mutex_lock(&thread_mutex, true);
88 : 62607 : }
89 : :
90 : 62607 : void mp_thread_unix_end_atomic_section(void) {
91 : 62607 : mp_thread_recursive_mutex_unlock(&thread_mutex);
92 : 62558 : }
93 : :
94 : : // this signal handler is used to scan the regs and stack of a thread
95 : 148 : static void mp_thread_gc(int signo, siginfo_t *info, void *context) {
96 : 148 : (void)info; // unused
97 : 148 : (void)context; // unused
98 [ + - ]: 148 : if (signo == MP_THREAD_GC_SIGNAL) {
99 : 148 : gc_helper_collect_regs_and_stack();
100 : : // We have access to the context (regs, stack) of the thread but it seems
101 : : // that we don't need the extra information, enough is captured by the
102 : : // gc_collect_regs_and_stack function above
103 : : // gc_collect_root((void**)context, sizeof(ucontext_t) / sizeof(uintptr_t));
104 : : #if MICROPY_ENABLE_PYSTACK
105 : : void **ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start);
106 : : gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *));
107 : : #endif
108 : : #if defined(__APPLE__)
109 : : sem_post(thread_signal_done_p);
110 : : #else
111 : 148 : sem_post(&thread_signal_done);
112 : : #endif
113 : : }
114 : 148 : }
115 : :
116 : : // On Android, pthread_cancel and pthread_setcanceltype are not implemented.
117 : : // To achieve that result a new signal handler responding on either
118 : : // (SIGRTMIN + 6) or SIGUSR2 is installed on every child thread. The sole
119 : : // purpose of this new signal handler is to terminate the thread in a safe
120 : : // asynchronous manner.
121 : :
122 : : #ifdef __ANDROID__
123 : : static void mp_thread_terminate(int signo, siginfo_t *info, void *context) {
124 : : pthread_exit(NULL);
125 : : }
126 : : #endif
127 : :
128 : 3456 : void mp_thread_init(void) {
129 : 3456 : pthread_key_create(&tls_key, NULL);
130 : 3456 : pthread_setspecific(tls_key, &mp_state_ctx.thread);
131 : :
132 : : // Needs to be a recursive mutex to emulate the behavior of
133 : : // BEGIN_ATOMIC_SECTION on bare metal.
134 : 3456 : mp_thread_recursive_mutex_init(&thread_mutex);
135 : :
136 : : // create first entry in linked list of all threads
137 : 3456 : thread = malloc(sizeof(mp_thread_t));
138 : 3456 : thread->id = pthread_self();
139 : 3456 : thread->ready = 1;
140 : 3456 : thread->arg = NULL;
141 : 3456 : thread->next = NULL;
142 : :
143 : : #if defined(__APPLE__)
144 : : snprintf(thread_signal_done_name, sizeof(thread_signal_done_name), "micropython_sem_%ld", (long)thread->id);
145 : : thread_signal_done_p = sem_open(thread_signal_done_name, O_CREAT | O_EXCL, 0666, 0);
146 : : #else
147 : 3456 : sem_init(&thread_signal_done, 0, 0);
148 : : #endif
149 : :
150 : : // enable signal handler for garbage collection
151 : 3456 : struct sigaction sa;
152 : 3456 : sa.sa_flags = SA_SIGINFO;
153 : 3456 : sa.sa_sigaction = mp_thread_gc;
154 : 3456 : sigemptyset(&sa.sa_mask);
155 : 3456 : sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL);
156 : :
157 : : // Install a signal handler for asynchronous termination if needed.
158 : : #if defined(__ANDROID__)
159 : : sa.sa_flags = SA_SIGINFO;
160 : : sa.sa_sigaction = mp_thread_terminate;
161 : : sigemptyset(&sa.sa_mask);
162 : : sigaction(MP_THREAD_TERMINATE_SIGNAL, &sa, NULL);
163 : : #endif
164 : 3456 : }
165 : :
166 : 3449 : void mp_thread_deinit(void) {
167 : 3449 : mp_thread_unix_begin_atomic_section();
168 [ - + ]: 3449 : while (thread->next != NULL) {
169 : 0 : mp_thread_t *th = thread;
170 : 0 : thread = thread->next;
171 : : #if defined(__ANDROID__)
172 : : pthread_kill(th->id, MP_THREAD_TERMINATE_SIGNAL);
173 : : #else
174 : 0 : pthread_cancel(th->id);
175 : : #endif
176 : 0 : free(th);
177 : : }
178 : 3449 : mp_thread_unix_end_atomic_section();
179 : : #if defined(__APPLE__)
180 : : sem_close(thread_signal_done_p);
181 : : sem_unlink(thread_signal_done_name);
182 : : #endif
183 [ - + ]: 3449 : assert(thread->id == pthread_self());
184 : 3449 : free(thread);
185 : 3449 : }
186 : :
187 : : // This function scans all pointers that are external to the current thread.
188 : : // It does this by signalling all other threads and getting them to scan their
189 : : // own registers and stack. Note that there may still be some edge cases left
190 : : // with race conditions and root-pointer scanning: a given thread may manipulate
191 : : // the global root pointers (in mp_state_ctx) while another thread is doing a
192 : : // garbage collection and tracing these pointers.
193 : 12732 : void mp_thread_gc_others(void) {
194 : 12732 : mp_thread_unix_begin_atomic_section();
195 [ + + ]: 25617 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
196 : 12885 : gc_collect_root(&th->arg, 1);
197 [ + + ]: 12885 : if (th->id == pthread_self()) {
198 : 12732 : continue;
199 : : }
200 [ + + ]: 153 : if (!th->ready) {
201 : 5 : continue;
202 : : }
203 : 148 : pthread_kill(th->id, MP_THREAD_GC_SIGNAL);
204 : : #if defined(__APPLE__)
205 : : sem_wait(thread_signal_done_p);
206 : : #else
207 : 148 : sem_wait(&thread_signal_done);
208 : : #endif
209 : : }
210 : 12732 : mp_thread_unix_end_atomic_section();
211 : 12732 : }
212 : :
213 : 462044496 : mp_state_thread_t *mp_thread_get_state(void) {
214 : 462044496 : return (mp_state_thread_t *)pthread_getspecific(tls_key);
215 : : }
216 : :
217 : 596 : void mp_thread_set_state(mp_state_thread_t *state) {
218 : 596 : pthread_setspecific(tls_key, state);
219 : 596 : }
220 : :
221 : 2 : mp_uint_t mp_thread_get_id(void) {
222 : 2 : return (mp_uint_t)pthread_self();
223 : : }
224 : :
225 : 596 : void mp_thread_start(void) {
226 : : // enable realtime priority if `-X realtime` command line parameter was set
227 : : #if defined(__APPLE__)
228 : : if (mp_thread_is_realtime_enabled) {
229 : : mp_thread_set_realtime();
230 : : }
231 : : #endif
232 : :
233 : : #if !defined(__ANDROID__)
234 : 596 : pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
235 : : #endif
236 : 596 : mp_thread_unix_begin_atomic_section();
237 [ + - ]: 6366 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
238 [ + + ]: 6366 : if (th->id == pthread_self()) {
239 : 596 : th->ready = 1;
240 : 596 : break;
241 : : }
242 : : }
243 : 596 : mp_thread_unix_end_atomic_section();
244 : 596 : }
245 : :
246 : 596 : mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) {
247 : : // default stack size is 8k machine-words
248 [ + + ]: 596 : if (*stack_size == 0) {
249 : 594 : *stack_size = 8192 * sizeof(void *);
250 : : }
251 : :
252 : : // minimum stack size is set by pthreads
253 [ + + ]: 596 : if (*stack_size < PTHREAD_STACK_MIN) {
254 : 2 : *stack_size = PTHREAD_STACK_MIN;
255 : : }
256 : :
257 : : // ensure there is enough stack to include a stack-overflow margin
258 [ - + ]: 596 : if (*stack_size < 2 * THREAD_STACK_OVERFLOW_MARGIN) {
259 : 0 : *stack_size = 2 * THREAD_STACK_OVERFLOW_MARGIN;
260 : : }
261 : :
262 : : // set thread attributes
263 : 596 : pthread_attr_t attr;
264 : 596 : int ret = pthread_attr_init(&attr);
265 [ - + ]: 596 : if (ret != 0) {
266 : 0 : goto er;
267 : : }
268 : 596 : ret = pthread_attr_setstacksize(&attr, *stack_size);
269 [ - + ]: 596 : if (ret != 0) {
270 : 0 : goto er;
271 : : }
272 : :
273 : 596 : ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
274 [ - + ]: 596 : if (ret != 0) {
275 : 0 : goto er;
276 : : }
277 : :
278 : 596 : mp_thread_unix_begin_atomic_section();
279 : :
280 : : // create thread
281 : 596 : pthread_t id;
282 : 596 : ret = pthread_create(&id, &attr, entry, arg);
283 [ - + ]: 596 : if (ret != 0) {
284 : 0 : mp_thread_unix_end_atomic_section();
285 : 0 : goto er;
286 : : }
287 : :
288 : : // adjust stack_size to provide room to recover from hitting the limit
289 : 596 : *stack_size -= THREAD_STACK_OVERFLOW_MARGIN;
290 : :
291 : : // add thread to linked list of all threads
292 : 596 : mp_thread_t *th = malloc(sizeof(mp_thread_t));
293 : 596 : th->id = id;
294 : 596 : th->ready = 0;
295 : 596 : th->arg = arg;
296 : 596 : th->next = thread;
297 : 596 : thread = th;
298 : :
299 : 596 : mp_thread_unix_end_atomic_section();
300 : :
301 : 596 : MP_STATIC_ASSERT(sizeof(mp_uint_t) >= sizeof(pthread_t));
302 : 596 : return (mp_uint_t)id;
303 : :
304 : 0 : er:
305 : 0 : mp_raise_OSError(ret);
306 : : }
307 : :
308 : 596 : void mp_thread_finish(void) {
309 : 596 : mp_thread_unix_begin_atomic_section();
310 : 596 : mp_thread_t *prev = NULL;
311 [ + - ]: 6187 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
312 [ + + ]: 6187 : if (th->id == pthread_self()) {
313 [ + + ]: 596 : if (prev == NULL) {
314 : 83 : thread = th->next;
315 : : } else {
316 : 513 : prev->next = th->next;
317 : : }
318 : 596 : free(th);
319 : 596 : break;
320 : : }
321 : 5591 : prev = th;
322 : : }
323 : 596 : mp_thread_unix_end_atomic_section();
324 : 596 : }
325 : :
326 : 3476 : void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
327 : 3476 : pthread_mutex_init(mutex, NULL);
328 : 3476 : }
329 : :
330 : 5844197 : int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
331 : 5844197 : int ret;
332 [ + + ]: 5844197 : if (wait) {
333 : 5844196 : ret = pthread_mutex_lock(mutex);
334 [ # + ]: 5846825 : if (ret == 0) {
335 : : return 1;
336 : : }
337 : : } else {
338 : 1 : ret = pthread_mutex_trylock(mutex);
339 [ + - ]: 1 : if (ret == 0) {
340 : : return 1;
341 [ - + ]: 1 : } else if (ret == EBUSY) {
342 : : return 0;
343 : : }
344 : : }
345 : 0 : return -ret;
346 : : }
347 : :
348 : 5846478 : void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
349 : 5846478 : pthread_mutex_unlock(mutex);
350 : : // TODO check return value
351 : 5826442 : }
352 : :
353 : : #if MICROPY_PY_THREAD_RECURSIVE_MUTEX
354 : :
355 : 11008 : void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *mutex) {
356 : 11008 : pthread_mutexattr_t attr;
357 : 11008 : pthread_mutexattr_init(&attr);
358 : 11008 : pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
359 : 11008 : pthread_mutex_init(mutex, &attr);
360 : 11008 : pthread_mutexattr_destroy(&attr);
361 : 11008 : }
362 : :
363 : 5566454 : int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *mutex, int wait) {
364 : 5566454 : return mp_thread_mutex_lock(mutex, wait);
365 : : }
366 : :
367 : 5569094 : void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *mutex) {
368 : 5569094 : mp_thread_mutex_unlock(mutex);
369 : 5569055 : }
370 : :
371 : : #endif // MICROPY_PY_THREAD_RECURSIVE_MUTEX
372 : :
373 : : #endif // MICROPY_PY_THREAD
374 : :
375 : : // this is used even when MICROPY_PY_THREAD is disabled
376 : :
377 : : #if defined(__APPLE__)
378 : : #include <mach/mach_error.h>
379 : : #include <mach/mach_time.h>
380 : : #include <mach/thread_act.h>
381 : : #include <mach/thread_policy.h>
382 : :
383 : : bool mp_thread_is_realtime_enabled;
384 : :
385 : : // based on https://developer.apple.com/library/archive/technotes/tn2169/_index.html
386 : : void mp_thread_set_realtime(void) {
387 : : mach_timebase_info_data_t timebase_info;
388 : :
389 : : mach_timebase_info(&timebase_info);
390 : :
391 : : const uint64_t NANOS_PER_MSEC = 1000000ULL;
392 : : double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
393 : :
394 : : thread_time_constraint_policy_data_t policy;
395 : : policy.period = 0;
396 : : policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
397 : : policy.constraint = (uint32_t)(10 * clock2abs);
398 : : policy.preemptible = FALSE;
399 : :
400 : : int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
401 : : THREAD_TIME_CONSTRAINT_POLICY,
402 : : (thread_policy_t)&policy,
403 : : THREAD_TIME_CONSTRAINT_POLICY_COUNT);
404 : :
405 : : if (kr != KERN_SUCCESS) {
406 : : mach_error("thread_policy_set:", kr);
407 : : }
408 : : }
409 : : #endif
|