Skip to content

Commit dbd54e0

Browse files
committed
unix: Implement garbage collection with threading.
This patch allows any given thread to do a proper garbage collection and scan all the pointers of all active threads.
1 parent 9172c0c commit dbd54e0

3 files changed

Lines changed: 118 additions & 4 deletions

File tree

unix/gccollect.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,22 @@ STATIC void gc_helper_get_regs(regs_t arr) {
136136

137137
#endif // MICROPY_GCREGS_SETJMP
138138

139-
void gc_collect(void) {
140-
//gc_dump_info();
141-
142-
gc_collect_start();
139+
void gc_collect_regs_and_stack(void) {
143140
regs_t regs;
144141
gc_helper_get_regs(regs);
145142
// GC stack (and regs because we captured them)
146143
void **regs_ptr = (void**)(void*)&regs;
147144
gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)&regs) / sizeof(uintptr_t));
145+
}
146+
147+
void gc_collect(void) {
148+
//gc_dump_info();
149+
150+
gc_collect_start();
151+
gc_collect_regs_and_stack();
152+
#if MICROPY_PY_THREAD
153+
mp_thread_gc_others();
154+
#endif
148155
#if MICROPY_EMIT_NATIVE
149156
mp_unix_mark_exec();
150157
#endif

unix/mpthreadport.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,88 @@
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+
3448
STATIC 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+
3667
void 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

41111
mp_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+
49130
void 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

75169
er:
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+
79185
void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
80186
pthread_mutex_init(mutex, NULL);
81187
}

unix/mpthreadport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@
3131
typedef pthread_mutex_t mp_thread_mutex_t;
3232

3333
void mp_thread_init(void);
34+
void mp_thread_gc_others(void);
3435

3536
#endif // __MICROPY_INCLUDED_UNIX_MPTHREADPORT_H__

0 commit comments

Comments
 (0)