2424 * THE SOFTWARE.
2525 */
2626
27+ #include <stdio.h>
28+ #include <stdlib.h>
2729#include <errno.h>
2830
2931#include "py/mpstate.h"
3032#include "py/mpthread.h"
33+ #include "py/gc.h"
3134
3235#if MICROPY_PY_THREAD
3336
37+ #include <signal.h>
38+ #include <sched.h>
39+
40+ // this structure forms a linked list, one node per active thread
41+ typedef struct _thread_t {
42+ pthread_t id ; // system id of thread
43+ int ready ; // whether the thread is ready and running
44+ void * arg ; // thread Python args, a GC root pointer
45+ struct _thread_t * next ;
46+ } thread_t ;
47+
3448STATIC pthread_key_t tls_key ;
3549
50+ // the mutex controls access to the linked list
51+ STATIC pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER ;
52+ STATIC thread_t * thread ;
53+
54+ // this is used to synchronise the signal handler of the thread
55+ // it's needed because we can't use any pthread calls in a signal handler
56+ STATIC volatile int thread_signal_done ;
57+
58+ // this signal handler is used to scan the regs and stack of a thread
59+ STATIC void mp_thread_gc (int signo ) {
60+ if (signo == SIGUSR1 ) {
61+ void gc_collect_regs_and_stack (void );
62+ gc_collect_regs_and_stack ();
63+ thread_signal_done = 1 ;
64+ }
65+ }
66+
3667void mp_thread_init (void ) {
3768 pthread_key_create (& tls_key , NULL );
3869 pthread_setspecific (tls_key , & mp_state_ctx .thread );
70+
71+ // create first entry in linked list of all threads
72+ thread = malloc (sizeof (thread_t ));
73+ thread -> id = pthread_self ();
74+ thread -> ready = 1 ;
75+ thread -> arg = NULL ;
76+ thread -> next = NULL ;
77+
78+ // enable signal handler for garbage collection
79+ struct sigaction sa ;
80+ sa .sa_flags = 0 ;
81+ sa .sa_handler = mp_thread_gc ;
82+ sigemptyset (& sa .sa_mask );
83+ sigaction (SIGUSR1 , & sa , NULL );
84+ }
85+
86+ // This function scans all pointers that are external to the current thread.
87+ // It does this by signalling all other threads and getting them to scan their
88+ // own registers and stack. Note that there may still be some edge cases left
89+ // with race conditions and root-pointer scanning: a given thread may manipulate
90+ // the global root pointers (in mp_state_ctx) while another thread is doing a
91+ // garbage collection and tracing these pointers.
92+ void mp_thread_gc_others (void ) {
93+ pthread_mutex_lock (& thread_mutex );
94+ for (thread_t * th = thread ; th != NULL ; th = th -> next ) {
95+ gc_collect_root (& th -> arg , 1 );
96+ if (th -> id == pthread_self ()) {
97+ continue ;
98+ }
99+ if (!th -> ready ) {
100+ continue ;
101+ }
102+ thread_signal_done = 0 ;
103+ pthread_kill (th -> id , SIGUSR1 );
104+ while (thread_signal_done == 0 ) {
105+ sched_yield ();
106+ }
107+ }
108+ pthread_mutex_unlock (& thread_mutex );
39109}
40110
41111mp_state_thread_t * mp_thread_get_state (void ) {
@@ -46,6 +116,17 @@ void mp_thread_set_state(void *state) {
46116 pthread_setspecific (tls_key , state );
47117}
48118
119+ void mp_thread_start (void ) {
120+ pthread_mutex_lock (& thread_mutex );
121+ for (thread_t * th = thread ; th != NULL ; th = th -> next ) {
122+ if (th -> id == pthread_self ()) {
123+ th -> ready = 1 ;
124+ break ;
125+ }
126+ }
127+ pthread_mutex_unlock (& thread_mutex );
128+ }
129+
49130void mp_thread_create (void * (* entry )(void * ), void * arg , size_t stack_size ) {
50131 // default stack size is 8k machine-words
51132 if (stack_size == 0 ) {
@@ -63,19 +144,44 @@ void mp_thread_create(void *(*entry)(void*), void *arg, size_t stack_size) {
63144 goto er ;
64145 }
65146
147+ pthread_mutex_lock (& thread_mutex );
148+
66149 // create thread
67150 pthread_t id ;
68151 ret = pthread_create (& id , & attr , entry , arg );
69152 if (ret != 0 ) {
153+ pthread_mutex_unlock (& thread_mutex );
70154 goto er ;
71155 }
72156
157+ // add thread to linked list of all threads
158+ thread_t * th = malloc (sizeof (thread_t ));
159+ th -> id = id ;
160+ th -> ready = 0 ;
161+ th -> arg = arg ;
162+ th -> next = thread ;
163+ thread = th ;
164+
165+ pthread_mutex_unlock (& thread_mutex );
166+
73167 return ;
74168
75169er :
76170 nlr_raise (mp_obj_new_exception_arg1 (& mp_type_OSError , MP_OBJ_NEW_SMALL_INT (ret )));
77171}
78172
173+ void mp_thread_finish (void ) {
174+ pthread_mutex_lock (& thread_mutex );
175+ // TODO unlink from list
176+ for (thread_t * th = thread ; th != NULL ; th = th -> next ) {
177+ if (th -> id == pthread_self ()) {
178+ th -> ready = 0 ;
179+ break ;
180+ }
181+ }
182+ pthread_mutex_unlock (& thread_mutex );
183+ }
184+
79185void mp_thread_mutex_init (mp_thread_mutex_t * mutex ) {
80186 pthread_mutex_init (mutex , NULL );
81187}
0 commit comments