Fix fork locking on win32 (#1141)

* Fix fork locking on win32

- __enable_threads / set __threaded in __proc_setup as threads are required for
  win32 subprocess management
- move mmi/fds locking out of pthread_atfork.c into fork.c so it's done anytime
  __threaded is set instead of being dependent of pthreads
- explicitly yoink _pthread_onfork_prepare, _pthread_onfork_parent, and
  _pthread_onfork_child in pthread_create.c so they are linked in in-case they
  are separated from _pthread_atfork

Big Thanks to @dfyz for help with locating the issue, testing, and devising a fix!

* fix child processes not being able to open files, initialize all necessary locks on fork
This commit is contained in:
Gavin Hayes 2024-04-25 23:01:27 -04:00 committed by GitHub
parent 6e6fc38935
commit 69db501c68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 23 deletions

View file

@ -20,6 +20,10 @@
pthread_spinlock_t _pthread_lock_obj;
void _pthread_init(void) {
(void)pthread_spin_init(&_pthread_lock_obj, 0);
}
void _pthread_lock(void) {
pthread_spin_lock(&_pthread_lock_obj);
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-nt.internal.h"
@ -34,12 +35,45 @@
#include "libc/nt/thread.h"
#include "libc/proc/proc.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
static void _onfork_prepare(void) {
if (_weaken(_pthread_onfork_prepare)) {
_weaken(_pthread_onfork_prepare)();
}
_pthread_lock();
__fds_lock();
__mmi_lock();
}
static void _onfork_parent(void) {
__mmi_unlock();
__fds_unlock();
_pthread_unlock();
if (_weaken(_pthread_onfork_parent)) {
_weaken(_pthread_onfork_parent)();
}
}
static void _onfork_child(void) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
extern pthread_mutex_t __mmi_lock_obj;
pthread_mutex_init(&__mmi_lock_obj, &attr);
pthread_mutex_init(&__fds_lock_obj, &attr);
pthread_mutexattr_destroy(&attr);
_pthread_init();
if (_weaken(_pthread_onfork_child)) {
_weaken(_pthread_onfork_child)();
}
}
int _fork(uint32_t dwCreationFlags) {
struct Dll *e;
int ax, dx, tid, parent;
@ -47,8 +81,8 @@ int _fork(uint32_t dwCreationFlags) {
BLOCK_SIGNALS;
if (IsWindows())
__proc_lock();
if (__threaded && _weaken(_pthread_onfork_prepare)) {
_weaken(_pthread_onfork_prepare)();
if (__threaded) {
_onfork_prepare();
}
if (!IsWindows()) {
ax = sys_fork();
@ -99,14 +133,14 @@ int _fork(uint32_t dwCreationFlags) {
atomic_store_explicit(&pt->pt_canceled, false, memory_order_relaxed);
// run user fork callbacks
if (__threaded && _weaken(_pthread_onfork_child)) {
_weaken(_pthread_onfork_child)();
if (__threaded) {
_onfork_child();
}
STRACE("fork() → 0 (child of %d)", parent);
} else {
// this is the parent process
if (__threaded && _weaken(_pthread_onfork_parent)) {
_weaken(_pthread_onfork_parent)();
if (__threaded) {
_onfork_parent();
}
if (IsWindows())
__proc_unlock();

View file

@ -233,6 +233,7 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
* Lazy initializes process tracker data structures and worker.
*/
static textwindows void __proc_setup(void) {
__enable_threads();
__proc.onbirth = CreateEvent(0, 0, 0, 0); // auto reset
__proc.haszombies = CreateEvent(0, 1, 0, 0); // manual reset
__proc.thread = CreateThread(0, 65536, __proc_worker, 0,

View file

@ -105,6 +105,7 @@ intptr_t _pthread_syshand(struct PosixThread *) libcesque;
long _pthread_cancel_ack(void) libcesque;
void _pthread_decimate(void) libcesque;
void _pthread_free(struct PosixThread *, bool) libcesque;
void _pthread_init(void) libcesque;
void _pthread_lock(void) libcesque;
void _pthread_onfork_child(void) libcesque;
void _pthread_onfork_parent(void) libcesque;

View file

@ -28,7 +28,6 @@
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/proc/proc.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/thread/posixthread.internal.h"
@ -47,8 +46,6 @@ static struct AtForks {
atomic_int allocated;
} _atforks;
extern pthread_spinlock_t _pthread_lock_obj;
static void _pthread_onfork(int i) {
struct AtFork *a;
unassert(0 <= i && i <= 2);
@ -65,27 +62,13 @@ static void _pthread_onfork(int i) {
void _pthread_onfork_prepare(void) {
_pthread_onfork(0);
_pthread_lock();
__fds_lock();
__mmi_lock();
}
void _pthread_onfork_parent(void) {
__mmi_unlock();
__fds_unlock();
_pthread_unlock();
_pthread_onfork(1);
}
void _pthread_onfork_child(void) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
extern pthread_mutex_t __mmi_lock_obj;
pthread_mutex_init(&__mmi_lock_obj, &attr);
pthread_mutex_init(&__fds_lock_obj, &attr);
pthread_mutexattr_destroy(&attr);
(void)pthread_spin_init(&_pthread_lock_obj, 0);
_pthread_onfork(2);
}

View file

@ -60,6 +60,9 @@ __static_yoink("nsync_mu_trylock");
__static_yoink("nsync_mu_rlock");
__static_yoink("nsync_mu_runlock");
__static_yoink("_pthread_atfork");
__static_yoink("_pthread_onfork_prepare");
__static_yoink("_pthread_onfork_parent");
__static_yoink("_pthread_onfork_child");
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000