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 : : #else
49 : : #define MP_THREAD_GC_SIGNAL (SIGUSR1)
50 : : #endif
51 : :
52 : : // This value seems to be about right for both 32-bit and 64-bit builds.
53 : : #define THREAD_STACK_OVERFLOW_MARGIN (8192)
54 : :
55 : : // this structure forms a linked list, one node per active thread
56 : : typedef struct _mp_thread_t {
57 : : pthread_t id; // system id of thread
58 : : int ready; // whether the thread is ready and running
59 : : void *arg; // thread Python args, a GC root pointer
60 : : struct _mp_thread_t *next;
61 : : } mp_thread_t;
62 : :
63 : : static pthread_key_t tls_key;
64 : :
65 : : // The mutex is used for any code in this port that needs to be thread safe.
66 : : // Specifically for thread management, access to the linked list is one example.
67 : : // But also, e.g. scheduler state.
68 : : static mp_thread_recursive_mutex_t thread_mutex;
69 : : static mp_thread_t *thread;
70 : :
71 : : // this is used to synchronise the signal handler of the thread
72 : : // it's needed because we can't use any pthread calls in a signal handler
73 : : #if defined(__APPLE__)
74 : : static char thread_signal_done_name[25];
75 : : static sem_t *thread_signal_done_p;
76 : : #else
77 : : static sem_t thread_signal_done;
78 : : #endif
79 : :
80 : 63117 : void mp_thread_unix_begin_atomic_section(void) {
81 : 63117 : mp_thread_recursive_mutex_lock(&thread_mutex, true);
82 : 63765 : }
83 : :
84 : 63765 : void mp_thread_unix_end_atomic_section(void) {
85 : 63765 : mp_thread_recursive_mutex_unlock(&thread_mutex);
86 : 63740 : }
87 : :
88 : : // this signal handler is used to scan the regs and stack of a thread
89 : 168 : static void mp_thread_gc(int signo, siginfo_t *info, void *context) {
90 : 168 : (void)info; // unused
91 : 168 : (void)context; // unused
92 [ + - ]: 168 : if (signo == MP_THREAD_GC_SIGNAL) {
93 : 168 : gc_helper_collect_regs_and_stack();
94 : : // We have access to the context (regs, stack) of the thread but it seems
95 : : // that we don't need the extra information, enough is captured by the
96 : : // gc_collect_regs_and_stack function above
97 : : // gc_collect_root((void**)context, sizeof(ucontext_t) / sizeof(uintptr_t));
98 : : #if MICROPY_ENABLE_PYSTACK
99 : : void **ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start);
100 : : gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *));
101 : : #endif
102 : : #if defined(__APPLE__)
103 : : sem_post(thread_signal_done_p);
104 : : #else
105 : 168 : sem_post(&thread_signal_done);
106 : : #endif
107 : : }
108 : 168 : }
109 : :
110 : 3456 : void mp_thread_init(void) {
111 : 3456 : pthread_key_create(&tls_key, NULL);
112 : 3456 : pthread_setspecific(tls_key, &mp_state_ctx.thread);
113 : :
114 : : // Needs to be a recursive mutex to emulate the behavior of
115 : : // BEGIN_ATOMIC_SECTION on bare metal.
116 : 3456 : mp_thread_recursive_mutex_init(&thread_mutex);
117 : :
118 : : // create first entry in linked list of all threads
119 : 3456 : thread = malloc(sizeof(mp_thread_t));
120 : 3456 : thread->id = pthread_self();
121 : 3456 : thread->ready = 1;
122 : 3456 : thread->arg = NULL;
123 : 3456 : thread->next = NULL;
124 : :
125 : : #if defined(__APPLE__)
126 : : snprintf(thread_signal_done_name, sizeof(thread_signal_done_name), "micropython_sem_%ld", (long)thread->id);
127 : : thread_signal_done_p = sem_open(thread_signal_done_name, O_CREAT | O_EXCL, 0666, 0);
128 : : #else
129 : 3456 : sem_init(&thread_signal_done, 0, 0);
130 : : #endif
131 : :
132 : : // enable signal handler for garbage collection
133 : 3456 : struct sigaction sa;
134 : 3456 : sa.sa_flags = SA_SIGINFO;
135 : 3456 : sa.sa_sigaction = mp_thread_gc;
136 : 3456 : sigemptyset(&sa.sa_mask);
137 : 3456 : sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL);
138 : 3456 : }
139 : :
140 : 3449 : void mp_thread_deinit(void) {
141 : 3449 : mp_thread_unix_begin_atomic_section();
142 [ - + ]: 3449 : while (thread->next != NULL) {
143 : 0 : mp_thread_t *th = thread;
144 : 0 : thread = thread->next;
145 : 0 : pthread_cancel(th->id);
146 : 0 : free(th);
147 : : }
148 : 3449 : mp_thread_unix_end_atomic_section();
149 : : #if defined(__APPLE__)
150 : : sem_close(thread_signal_done_p);
151 : : sem_unlink(thread_signal_done_name);
152 : : #endif
153 [ - + ]: 3449 : assert(thread->id == pthread_self());
154 : 3449 : free(thread);
155 : 3449 : }
156 : :
157 : : // This function scans all pointers that are external to the current thread.
158 : : // It does this by signalling all other threads and getting them to scan their
159 : : // own registers and stack. Note that there may still be some edge cases left
160 : : // with race conditions and root-pointer scanning: a given thread may manipulate
161 : : // the global root pointers (in mp_state_ctx) while another thread is doing a
162 : : // garbage collection and tracing these pointers.
163 : 12728 : void mp_thread_gc_others(void) {
164 : 12728 : mp_thread_unix_begin_atomic_section();
165 [ + + ]: 25627 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
166 : 12899 : gc_collect_root(&th->arg, 1);
167 [ + + ]: 12899 : if (th->id == pthread_self()) {
168 : 12728 : continue;
169 : : }
170 [ + + ]: 171 : if (!th->ready) {
171 : 3 : continue;
172 : : }
173 : 168 : pthread_kill(th->id, MP_THREAD_GC_SIGNAL);
174 : : #if defined(__APPLE__)
175 : : sem_wait(thread_signal_done_p);
176 : : #else
177 : 168 : sem_wait(&thread_signal_done);
178 : : #endif
179 : : }
180 : 12728 : mp_thread_unix_end_atomic_section();
181 : 12728 : }
182 : :
183 : 444532498 : mp_state_thread_t *mp_thread_get_state(void) {
184 : 444532498 : return (mp_state_thread_t *)pthread_getspecific(tls_key);
185 : : }
186 : :
187 : 596 : void mp_thread_set_state(mp_state_thread_t *state) {
188 : 596 : pthread_setspecific(tls_key, state);
189 : 596 : }
190 : :
191 : 2 : mp_uint_t mp_thread_get_id(void) {
192 : 2 : return (mp_uint_t)pthread_self();
193 : : }
194 : :
195 : 596 : void mp_thread_start(void) {
196 : : // enable realtime priority if `-X realtime` command line parameter was set
197 : : #if defined(__APPLE__)
198 : : if (mp_thread_is_realtime_enabled) {
199 : : mp_thread_set_realtime();
200 : : }
201 : : #endif
202 : :
203 : 596 : pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
204 : 596 : mp_thread_unix_begin_atomic_section();
205 [ + - ]: 8872 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
206 [ + + ]: 8872 : if (th->id == pthread_self()) {
207 : 596 : th->ready = 1;
208 : 596 : break;
209 : : }
210 : : }
211 : 596 : mp_thread_unix_end_atomic_section();
212 : 596 : }
213 : :
214 : 596 : mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) {
215 : : // default stack size is 8k machine-words
216 [ + + ]: 596 : if (*stack_size == 0) {
217 : 594 : *stack_size = 8192 * sizeof(void *);
218 : : }
219 : :
220 : : // minimum stack size is set by pthreads
221 [ + + ]: 596 : if (*stack_size < PTHREAD_STACK_MIN) {
222 : 2 : *stack_size = PTHREAD_STACK_MIN;
223 : : }
224 : :
225 : : // ensure there is enough stack to include a stack-overflow margin
226 [ - + ]: 596 : if (*stack_size < 2 * THREAD_STACK_OVERFLOW_MARGIN) {
227 : 0 : *stack_size = 2 * THREAD_STACK_OVERFLOW_MARGIN;
228 : : }
229 : :
230 : : // set thread attributes
231 : 596 : pthread_attr_t attr;
232 : 596 : int ret = pthread_attr_init(&attr);
233 [ - + ]: 596 : if (ret != 0) {
234 : 0 : goto er;
235 : : }
236 : 596 : ret = pthread_attr_setstacksize(&attr, *stack_size);
237 [ - + ]: 596 : if (ret != 0) {
238 : 0 : goto er;
239 : : }
240 : :
241 : 596 : ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
242 [ - + ]: 596 : if (ret != 0) {
243 : 0 : goto er;
244 : : }
245 : :
246 : 596 : mp_thread_unix_begin_atomic_section();
247 : :
248 : : // create thread
249 : 596 : pthread_t id;
250 : 596 : ret = pthread_create(&id, &attr, entry, arg);
251 [ - + ]: 596 : if (ret != 0) {
252 : 0 : mp_thread_unix_end_atomic_section();
253 : 0 : goto er;
254 : : }
255 : :
256 : : // adjust stack_size to provide room to recover from hitting the limit
257 : 596 : *stack_size -= THREAD_STACK_OVERFLOW_MARGIN;
258 : :
259 : : // add thread to linked list of all threads
260 : 596 : mp_thread_t *th = malloc(sizeof(mp_thread_t));
261 : 596 : th->id = id;
262 : 596 : th->ready = 0;
263 : 596 : th->arg = arg;
264 : 596 : th->next = thread;
265 : 596 : thread = th;
266 : :
267 : 596 : mp_thread_unix_end_atomic_section();
268 : :
269 : 596 : MP_STATIC_ASSERT(sizeof(mp_uint_t) >= sizeof(pthread_t));
270 : 596 : return (mp_uint_t)id;
271 : :
272 : 0 : er:
273 : 0 : mp_raise_OSError(ret);
274 : : }
275 : :
276 : 596 : void mp_thread_finish(void) {
277 : 596 : mp_thread_unix_begin_atomic_section();
278 : 596 : mp_thread_t *prev = NULL;
279 [ + - ]: 8673 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
280 [ + + ]: 8673 : if (th->id == pthread_self()) {
281 [ + + ]: 596 : if (prev == NULL) {
282 : 84 : thread = th->next;
283 : : } else {
284 : 512 : prev->next = th->next;
285 : : }
286 : 596 : free(th);
287 : 596 : break;
288 : : }
289 : 8077 : prev = th;
290 : : }
291 : 596 : mp_thread_unix_end_atomic_section();
292 : 596 : }
293 : :
294 : 3476 : void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
295 : 3476 : pthread_mutex_init(mutex, NULL);
296 : 3476 : }
297 : :
298 : 5819210 : int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
299 : 5819210 : int ret;
300 [ + + ]: 5819210 : if (wait) {
301 : 5819209 : ret = pthread_mutex_lock(mutex);
302 [ # + ]: 5822049 : if (ret == 0) {
303 : : return 1;
304 : : }
305 : : } else {
306 : 1 : ret = pthread_mutex_trylock(mutex);
307 [ + - ]: 1 : if (ret == 0) {
308 : : return 1;
309 [ - + ]: 1 : } else if (ret == EBUSY) {
310 : : return 0;
311 : : }
312 : : }
313 : 0 : return -ret;
314 : : }
315 : :
316 : 5821823 : void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
317 : 5821823 : pthread_mutex_unlock(mutex);
318 : : // TODO check return value
319 : 5820716 : }
320 : :
321 : : #if MICROPY_PY_THREAD_RECURSIVE_MUTEX
322 : :
323 : 11008 : void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *mutex) {
324 : 11008 : pthread_mutexattr_t attr;
325 : 11008 : pthread_mutexattr_init(&attr);
326 : 11008 : pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
327 : 11008 : pthread_mutex_init(mutex, &attr);
328 : 11008 : pthread_mutexattr_destroy(&attr);
329 : 11008 : }
330 : :
331 : 5562235 : int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *mutex, int wait) {
332 : 5562235 : return mp_thread_mutex_lock(mutex, wait);
333 : : }
334 : :
335 : 5565176 : void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *mutex) {
336 : 5565176 : mp_thread_mutex_unlock(mutex);
337 : 5565272 : }
338 : :
339 : : #endif // MICROPY_PY_THREAD_RECURSIVE_MUTEX
340 : :
341 : : #endif // MICROPY_PY_THREAD
342 : :
343 : : // this is used even when MICROPY_PY_THREAD is disabled
344 : :
345 : : #if defined(__APPLE__)
346 : : #include <mach/mach_error.h>
347 : : #include <mach/mach_time.h>
348 : : #include <mach/thread_act.h>
349 : : #include <mach/thread_policy.h>
350 : :
351 : : bool mp_thread_is_realtime_enabled;
352 : :
353 : : // based on https://developer.apple.com/library/archive/technotes/tn2169/_index.html
354 : : void mp_thread_set_realtime(void) {
355 : : mach_timebase_info_data_t timebase_info;
356 : :
357 : : mach_timebase_info(&timebase_info);
358 : :
359 : : const uint64_t NANOS_PER_MSEC = 1000000ULL;
360 : : double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
361 : :
362 : : thread_time_constraint_policy_data_t policy;
363 : : policy.period = 0;
364 : : policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
365 : : policy.constraint = (uint32_t)(10 * clock2abs);
366 : : policy.preemptible = FALSE;
367 : :
368 : : int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
369 : : THREAD_TIME_CONSTRAINT_POLICY,
370 : : (thread_policy_t)&policy,
371 : : THREAD_TIME_CONSTRAINT_POLICY_COUNT);
372 : :
373 : : if (kr != KERN_SUCCESS) {
374 : : mach_error("thread_policy_set:", kr);
375 : : }
376 : : }
377 : : #endif
|