Make garbage collection thread safe

- You can now use _gc(malloc()) in multithreaded programs
- This change fixes a bug where fork() on NT disabled TLS
- Fixed TLS code morphing on XNU/NT, for R8-R15 registers
This commit is contained in:
Justine Tunney 2022-09-08 02:33:01 -07:00
parent 571c2c3c69
commit 0e2b1bfeed
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
37 changed files with 310 additions and 189 deletions

View file

@ -20,8 +20,6 @@
#include "libc/dce.h"
#include "libc/notice.inc"
#define INITIAL_CAPACITY 4
nop
// Invokes deferred function calls.
@ -34,9 +32,12 @@
//
// @param rax,rdx,xmm0,xmm1,st0,st1 is return value
// @see test/libc/runtime/gc_test.c
__gc: decq __garbage(%rip)
mov __garbage(%rip),%r8
mov __garbage+16(%rip),%r9
// @threadsafe
__gc: mov %fs:0,%rcx # __get_tls()
mov 0x18(%rcx),%rcx # cthread_t::garbages
decl (%rcx) # ++g->i
mov (%rcx),%r8d # r8 = g->i
mov 8(%rcx),%r9 # r9 = g->p
js 9f
shl $5,%r8
lea (%r9,%r8),%r8
@ -55,25 +56,5 @@ __gc: decq __garbage(%rip)
mov -8(%rbp),%rax
leave
ret
9: hlt
9: ud2
.endfn __gc,globl,hidden
.bss
.align 8
__garbage:
.quad 0 # garbage.i
.quad 0 # garbage.n
.quad 0 # garbage.p
.rept INITIAL_CAPACITY
.quad 0 # garbage.p[𝑖].frame
.quad 0 # garbage.p[𝑖].fn
.quad 0 # garbage.p[𝑖].arg
.quad 0 # garbage.p[𝑖].ret
.endr
.endobj __garbage,globl,hidden
.previous
.init.start 100,_init_garbage
movb $INITIAL_CAPACITY,__garbage+8(%rip)
movl $__garbage+24,__garbage+16(%rip)
.init.end 100,_init_garbage

View file

@ -12,13 +12,10 @@ struct Garbage {
};
struct Garbages {
size_t i, n;
int i, n;
struct Garbage *p;
struct Garbage initmem[1];
};
extern struct Garbages __garbage;
int64_t __gc(void);
COSMOPOLITAN_C_END_

View file

@ -28,29 +28,33 @@
// @param esi is returned by setjmp() invocation (coerced nonzero)
// @assume system five nexgen32e abi conformant
// @see examples/ctrlc.c
// @threadsafe
// @noreturn
_gclongjmp:
push %rbp
mov %rsp,%rbp
.profilable
lea __garbage(%rip),%r12
mov (%r12),%r13 # garbage.i
test %r13,%r13
mov %fs:0,%r12 # __get_tls()
mov 0x18(%r12),%r12 # cthread_t::garbages
test %r12,%r12
jz 0f
movl (%r12),%r13d # garbages.i
test %r13d,%r13d
jnz .L.unwind.destructors
0: jmp longjmp
.L.unwind.destructors:
push %rdi
push %rsi
mov 16(%r12),%r14 # garbage.p
mov 8(%r12),%r14 # garbages.p
mov (%rdi),%r15 # jmp_buf[0] is new %rsp
shl $5,%r13 # log2(sizeof(struct Garbage))
1: sub $32,%r13 # 𝑖--
js 2f
cmp (%r14,%r13),%r15 # new %rsp > garbage.p[𝑖].frame
cmp (%r14,%r13),%r15 # new %rsp > garbages.p[𝑖].frame
jbe 2f
mov 16(%r14,%r13),%rdi # garbage.p[𝑖].arg
callq *8(%r14,%r13) # garbage.p[𝑖].fn
decq (%r12) # garbage.i--
mov 16(%r14,%r13),%rdi # garbages.p[𝑖].arg
callq *8(%r14,%r13) # garbages.p[𝑖].fn
decl (%r12) # garbages.i--
jmp 1b
2: pop %rsi
pop %rdi

View file

@ -7,6 +7,7 @@ extern int __threaded;
extern bool __tls_enabled;
extern unsigned __tls_index;
void TlsIsRequired(void);
void *__initialize_tls(char[64]);
void __install_tls(char[64]);