mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-02 17:28:30 +00:00
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:
parent
571c2c3c69
commit
0e2b1bfeed
37 changed files with 310 additions and 189 deletions
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
|
@ -24,7 +25,9 @@
|
|||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/gc.internal.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/clone.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -56,6 +59,7 @@ static int PosixThread(void *arg, int tid) {
|
|||
if (weaken(_pthread_key_destruct)) {
|
||||
weaken(_pthread_key_destruct)(0);
|
||||
}
|
||||
cthread_ungarbage();
|
||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
|
||||
kPosixThreadDetached) {
|
||||
atomic_store_explicit(&pt->status, kPosixThreadZombie,
|
||||
|
@ -108,6 +112,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
|||
int rc, e = errno;
|
||||
struct PosixThread *pt;
|
||||
pthread_attr_t default_attr;
|
||||
TlsIsRequired();
|
||||
_pthread_zombies_decimate();
|
||||
|
||||
// default attributes
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
@ -38,7 +39,7 @@ wontreturn void pthread_exit(void *rc) {
|
|||
struct PosixThread *pt;
|
||||
if ((pt = ((cthread_t)__get_tls())->pthread)) {
|
||||
pt->rc = rc;
|
||||
longjmp(pt->exiter, 1);
|
||||
_gclongjmp(pt->exiter, 1);
|
||||
} else {
|
||||
_Exit1((int)(intptr_t)rc);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "libc/sysv/consts/clone.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
|
@ -54,6 +55,20 @@
|
|||
#define _TIBZ sizeof(struct cthread_descriptor_t)
|
||||
#define _MEMZ ROUNDUP(_TLSZ + _TIBZ, alignof(struct cthread_descriptor_t))
|
||||
|
||||
struct spawner {
|
||||
int (*fun)(void *, int);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static int Spawner(void *arg, int tid) {
|
||||
int rc;
|
||||
struct spawner *spawner = arg;
|
||||
rc = spawner->fun(spawner->arg, tid);
|
||||
cthread_ungarbage();
|
||||
free(spawner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns thread, e.g.
|
||||
*
|
||||
|
@ -78,6 +93,9 @@
|
|||
*/
|
||||
int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
|
||||
struct spawn *th, ths;
|
||||
struct spawner *spawner;
|
||||
TlsIsRequired();
|
||||
if (!fun) return einval();
|
||||
|
||||
// we need to to clobber the output memory before calling clone, since
|
||||
// there's no guarantee clone() won't suspend the parent, and focus on
|
||||
|
@ -102,11 +120,14 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (clone(fun, th->stk, GetStackSize() - 16 /* openbsd:stackbound */,
|
||||
spawner = malloc(sizeof(struct spawner));
|
||||
spawner->fun = fun;
|
||||
spawner->arg = arg;
|
||||
if (clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */,
|
||||
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID,
|
||||
arg, &th->ptid, th->tib, th->ctid) == -1) {
|
||||
spawner, &th->ptid, th->tib, th->ctid) == -1) {
|
||||
_freestack(th->stk);
|
||||
free(th->tls);
|
||||
return -1;
|
||||
|
|
|
@ -28,7 +28,7 @@ struct FtraceTls { /* 16 */
|
|||
struct cthread_descriptor_t {
|
||||
struct cthread_descriptor_t *self; /* 0x00 */
|
||||
struct FtraceTls ftrace; /* 0x08 */
|
||||
void *garbages; /* 0x10 */
|
||||
void *garbages; /* 0x18 */
|
||||
locale_t locale; /* 0x20 */
|
||||
pthread_t pthread; /* 0x28 */
|
||||
struct cthread_descriptor_t *self2; /* 0x30 */
|
||||
|
@ -64,6 +64,7 @@ int cthread_sem_wait(cthread_sem_t *, int, const struct timespec *);
|
|||
int cthread_sem_signal(cthread_sem_t *);
|
||||
int cthread_memory_wait32(int *, int, const struct timespec *);
|
||||
int cthread_memory_wake32(int *, int);
|
||||
void cthread_ungarbage(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
34
libc/thread/ungarbage.c
Normal file
34
libc/thread/ungarbage.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/gc.internal.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void cthread_ungarbage(void) {
|
||||
struct Garbages *g;
|
||||
if ((g = ((cthread_t)__get_tls())->garbages)) {
|
||||
// _pthread_exit() uses _gclongjmp() so if this assertion fails,
|
||||
// then the likely cause is the thread used gc() with longjmp().
|
||||
assert(!g->i);
|
||||
free(g->p);
|
||||
free(g);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue