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 pthread_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 : 62184 : void mp_thread_unix_begin_atomic_section(void) {
81 : 62184 : pthread_mutex_lock(&thread_mutex);
82 : 63516 : }
83 : :
84 : 63516 : void mp_thread_unix_end_atomic_section(void) {
85 : 63516 : pthread_mutex_unlock(&thread_mutex);
86 : 63482 : }
87 : :
88 : : // this signal handler is used to scan the regs and stack of a thread
89 : 174 : static void mp_thread_gc(int signo, siginfo_t *info, void *context) {
90 : 174 : (void)info; // unused
91 : 174 : (void)context; // unused
92 [ + - ]: 174 : if (signo == MP_THREAD_GC_SIGNAL) {
93 : 174 : 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 : 174 : sem_post(&thread_signal_done);
106 : : #endif
107 : : }
108 : 174 : }
109 : :
110 : 3420 : void mp_thread_init(void) {
111 : 3420 : pthread_key_create(&tls_key, NULL);
112 : 3420 : 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 : 3420 : pthread_mutexattr_t thread_mutex_attr;
117 : 3420 : pthread_mutexattr_init(&thread_mutex_attr);
118 : 3420 : pthread_mutexattr_settype(&thread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
119 : 3420 : pthread_mutex_init(&thread_mutex, &thread_mutex_attr);
120 : :
121 : : // create first entry in linked list of all threads
122 : 3420 : thread = malloc(sizeof(mp_thread_t));
123 : 3420 : thread->id = pthread_self();
124 : 3420 : thread->ready = 1;
125 : 3420 : thread->arg = NULL;
126 : 3420 : thread->next = NULL;
127 : :
128 : : #if defined(__APPLE__)
129 : : snprintf(thread_signal_done_name, sizeof(thread_signal_done_name), "micropython_sem_%ld", (long)thread->id);
130 : : thread_signal_done_p = sem_open(thread_signal_done_name, O_CREAT | O_EXCL, 0666, 0);
131 : : #else
132 : 3420 : sem_init(&thread_signal_done, 0, 0);
133 : : #endif
134 : :
135 : : // enable signal handler for garbage collection
136 : 3420 : struct sigaction sa;
137 : 3420 : sa.sa_flags = SA_SIGINFO;
138 : 3420 : sa.sa_sigaction = mp_thread_gc;
139 : 3420 : sigemptyset(&sa.sa_mask);
140 : 3420 : sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL);
141 : 3420 : }
142 : :
143 : 3416 : void mp_thread_deinit(void) {
144 : 3416 : mp_thread_unix_begin_atomic_section();
145 [ + + ]: 3417 : while (thread->next != NULL) {
146 : 1 : mp_thread_t *th = thread;
147 : 1 : thread = thread->next;
148 : 1 : pthread_cancel(th->id);
149 : 1 : free(th);
150 : : }
151 : 3416 : mp_thread_unix_end_atomic_section();
152 : : #if defined(__APPLE__)
153 : : sem_close(thread_signal_done_p);
154 : : sem_unlink(thread_signal_done_name);
155 : : #endif
156 [ - + ]: 3416 : assert(thread->id == pthread_self());
157 : 3416 : free(thread);
158 : 3416 : }
159 : :
160 : : // This function scans all pointers that are external to the current thread.
161 : : // It does this by signalling all other threads and getting them to scan their
162 : : // own registers and stack. Note that there may still be some edge cases left
163 : : // with race conditions and root-pointer scanning: a given thread may manipulate
164 : : // the global root pointers (in mp_state_ctx) while another thread is doing a
165 : : // garbage collection and tracing these pointers.
166 : 12721 : void mp_thread_gc_others(void) {
167 : 12721 : mp_thread_unix_begin_atomic_section();
168 [ + + ]: 25622 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
169 : 12901 : gc_collect_root(&th->arg, 1);
170 [ + + ]: 12901 : if (th->id == pthread_self()) {
171 : 12721 : continue;
172 : : }
173 [ + + ]: 180 : if (!th->ready) {
174 : 6 : continue;
175 : : }
176 : 174 : pthread_kill(th->id, MP_THREAD_GC_SIGNAL);
177 : : #if defined(__APPLE__)
178 : : sem_wait(thread_signal_done_p);
179 : : #else
180 : 174 : sem_wait(&thread_signal_done);
181 : : #endif
182 : : }
183 : 12721 : mp_thread_unix_end_atomic_section();
184 : 12721 : }
185 : :
186 : 486857006 : mp_state_thread_t *mp_thread_get_state(void) {
187 : 486857006 : return (mp_state_thread_t *)pthread_getspecific(tls_key);
188 : : }
189 : :
190 : 596 : void mp_thread_set_state(mp_state_thread_t *state) {
191 : 596 : pthread_setspecific(tls_key, state);
192 : 596 : }
193 : :
194 : 2 : mp_uint_t mp_thread_get_id(void) {
195 : 2 : return (mp_uint_t)pthread_self();
196 : : }
197 : :
198 : 596 : void mp_thread_start(void) {
199 : : // enable realtime priority if `-X realtime` command line parameter was set
200 : : #if defined(__APPLE__)
201 : : if (mp_thread_is_realtime_enabled) {
202 : : mp_thread_set_realtime();
203 : : }
204 : : #endif
205 : :
206 : 596 : pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
207 : 596 : mp_thread_unix_begin_atomic_section();
208 [ + - ]: 38359 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
209 [ + + ]: 38359 : if (th->id == pthread_self()) {
210 : 596 : th->ready = 1;
211 : 596 : break;
212 : : }
213 : : }
214 : 596 : mp_thread_unix_end_atomic_section();
215 : 596 : }
216 : :
217 : 596 : mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) {
218 : : // default stack size is 8k machine-words
219 [ + + ]: 596 : if (*stack_size == 0) {
220 : 594 : *stack_size = 8192 * sizeof(void *);
221 : : }
222 : :
223 : : // minimum stack size is set by pthreads
224 [ + + ]: 596 : if (*stack_size < PTHREAD_STACK_MIN) {
225 : 2 : *stack_size = PTHREAD_STACK_MIN;
226 : : }
227 : :
228 : : // ensure there is enough stack to include a stack-overflow margin
229 [ - + ]: 596 : if (*stack_size < 2 * THREAD_STACK_OVERFLOW_MARGIN) {
230 : 0 : *stack_size = 2 * THREAD_STACK_OVERFLOW_MARGIN;
231 : : }
232 : :
233 : : // set thread attributes
234 : 596 : pthread_attr_t attr;
235 : 596 : int ret = pthread_attr_init(&attr);
236 [ - + ]: 596 : if (ret != 0) {
237 : 0 : goto er;
238 : : }
239 : 596 : ret = pthread_attr_setstacksize(&attr, *stack_size);
240 [ - + ]: 596 : if (ret != 0) {
241 : 0 : goto er;
242 : : }
243 : :
244 : 596 : ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
245 [ - + ]: 596 : if (ret != 0) {
246 : 0 : goto er;
247 : : }
248 : :
249 : 596 : mp_thread_unix_begin_atomic_section();
250 : :
251 : : // create thread
252 : 596 : pthread_t id;
253 : 596 : ret = pthread_create(&id, &attr, entry, arg);
254 [ - + ]: 596 : if (ret != 0) {
255 : 0 : mp_thread_unix_end_atomic_section();
256 : 0 : goto er;
257 : : }
258 : :
259 : : // adjust stack_size to provide room to recover from hitting the limit
260 : 596 : *stack_size -= THREAD_STACK_OVERFLOW_MARGIN;
261 : :
262 : : // add thread to linked list of all threads
263 : 596 : mp_thread_t *th = malloc(sizeof(mp_thread_t));
264 : 596 : th->id = id;
265 : 596 : th->ready = 0;
266 : 596 : th->arg = arg;
267 : 596 : th->next = thread;
268 : 596 : thread = th;
269 : :
270 : 596 : mp_thread_unix_end_atomic_section();
271 : :
272 : 596 : MP_STATIC_ASSERT(sizeof(mp_uint_t) >= sizeof(pthread_t));
273 : 596 : return (mp_uint_t)id;
274 : :
275 : 0 : er:
276 : 0 : mp_raise_OSError(ret);
277 : : }
278 : :
279 : 594 : void mp_thread_finish(void) {
280 : 594 : mp_thread_unix_begin_atomic_section();
281 : 595 : mp_thread_t *prev = NULL;
282 [ + - ]: 38045 : for (mp_thread_t *th = thread; th != NULL; th = th->next) {
283 [ + + ]: 38045 : if (th->id == pthread_self()) {
284 [ + + ]: 595 : if (prev == NULL) {
285 : 41 : thread = th->next;
286 : : } else {
287 : 554 : prev->next = th->next;
288 : : }
289 : 595 : free(th);
290 : 595 : break;
291 : : }
292 : 37450 : prev = th;
293 : : }
294 : 595 : mp_thread_unix_end_atomic_section();
295 : 595 : }
296 : :
297 : 10956 : void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
298 : 10956 : pthread_mutex_init(mutex, NULL);
299 : 10956 : }
300 : :
301 : 5576020 : int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
302 : 5576020 : int ret;
303 [ + + ]: 5576020 : if (wait) {
304 : 5576019 : ret = pthread_mutex_lock(mutex);
305 [ # + ]: 5577809 : if (ret == 0) {
306 : : return 1;
307 : : }
308 : : } else {
309 : 1 : ret = pthread_mutex_trylock(mutex);
310 [ + - ]: 1 : if (ret == 0) {
311 : : return 1;
312 [ - + ]: 1 : } else if (ret == EBUSY) {
313 : : return 0;
314 : : }
315 : : }
316 : 0 : return -ret;
317 : : }
318 : :
319 : 5577755 : void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
320 : 5577755 : pthread_mutex_unlock(mutex);
321 : : // TODO check return value
322 : 5577298 : }
323 : :
324 : : #endif // MICROPY_PY_THREAD
325 : :
326 : : // this is used even when MICROPY_PY_THREAD is disabled
327 : :
328 : : #if defined(__APPLE__)
329 : : #include <mach/mach_error.h>
330 : : #include <mach/mach_time.h>
331 : : #include <mach/thread_act.h>
332 : : #include <mach/thread_policy.h>
333 : :
334 : : bool mp_thread_is_realtime_enabled;
335 : :
336 : : // based on https://developer.apple.com/library/archive/technotes/tn2169/_index.html
337 : : void mp_thread_set_realtime(void) {
338 : : mach_timebase_info_data_t timebase_info;
339 : :
340 : : mach_timebase_info(&timebase_info);
341 : :
342 : : const uint64_t NANOS_PER_MSEC = 1000000ULL;
343 : : double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
344 : :
345 : : thread_time_constraint_policy_data_t policy;
346 : : policy.period = 0;
347 : : policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
348 : : policy.constraint = (uint32_t)(10 * clock2abs);
349 : : policy.preemptible = FALSE;
350 : :
351 : : int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
352 : : THREAD_TIME_CONSTRAINT_POLICY,
353 : : (thread_policy_t)&policy,
354 : : THREAD_TIME_CONSTRAINT_POLICY_COUNT);
355 : :
356 : : if (kr != KERN_SUCCESS) {
357 : : mach_error("thread_policy_set:", kr);
358 : : }
359 : : }
360 : : #endif
|