From df7f245cf88a0dcfaf08eb92c390fc4bf6ede17f Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 1 Jun 2025 01:41:09 +0300 Subject: [PATCH] LibC: Implement pthread_atfork Again this code is not tested but *feels* right :D --- userspace/libraries/LibC/include/pthread.h | 5 ++ userspace/libraries/LibC/pthread.cpp | 96 ++++++++++++++++++++++ userspace/libraries/LibC/unistd.cpp | 7 +- 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/userspace/libraries/LibC/include/pthread.h b/userspace/libraries/LibC/include/pthread.h index d654056a..d8ecd745 100644 --- a/userspace/libraries/LibC/include/pthread.h +++ b/userspace/libraries/LibC/include/pthread.h @@ -82,6 +82,11 @@ struct uthread #define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t){ { PTHREAD_MUTEX_DEFAULT, 0 }, 0, 0 } #define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t){ { 0 }, 0, 0 } +#define _PTHREAD_ATFORK_PREPARE 0 +#define _PTHREAD_ATFORK_PARENT 1 +#define _PTHREAD_ATFORK_CHILD 2 +void _pthread_call_atfork(int state); + int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)); int pthread_attr_destroy(pthread_attr_t* attr); int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate); diff --git a/userspace/libraries/LibC/pthread.cpp b/userspace/libraries/LibC/pthread.cpp index 6e553761..f435b42e 100644 --- a/userspace/libraries/LibC/pthread.cpp +++ b/userspace/libraries/LibC/pthread.cpp @@ -470,6 +470,102 @@ int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) return 0; } +struct pthread_atfork_t +{ + void (*function)(); + pthread_atfork_t* next; +}; +static pthread_atfork_t* s_atfork_prepare = nullptr; +static pthread_atfork_t* s_atfork_parent = nullptr; +static pthread_atfork_t* s_atfork_child = nullptr; +static pthread_mutex_t s_atfork_mutex = PTHREAD_MUTEX_INITIALIZER; + +void _pthread_call_atfork(int state) +{ + pthread_mutex_lock(&s_atfork_mutex); + + pthread_atfork_t* list = nullptr; + switch (state) + { + case _PTHREAD_ATFORK_PREPARE: list = s_atfork_prepare; break; + case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break; + case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break; + default: + ASSERT_NOT_REACHED(); + } + + for (; list; list = list->next) + list->function(); + + pthread_mutex_unlock(&s_atfork_mutex); +} + +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) +{ + pthread_atfork_t* prepare_entry = nullptr; + if (prepare != nullptr) + prepare_entry = static_cast(malloc(sizeof(pthread_attr_t))); + + pthread_atfork_t* parent_entry = nullptr; + if (parent != nullptr) + parent_entry = static_cast(malloc(sizeof(pthread_attr_t))); + + pthread_atfork_t* child_entry = nullptr; + if (child != nullptr) + child_entry = static_cast(malloc(sizeof(pthread_attr_t))); + + if ((prepare && !prepare_entry) || (parent && !parent_entry) || (child && !child_entry)) + { + if (prepare_entry) + free(prepare_entry); + if (parent_entry) + free(parent_entry); + if (child_entry) + free(child_entry); + return ENOMEM; + } + + const auto prepend_atfork = + [](pthread_atfork_t*& list, pthread_atfork_t* entry) + { + entry->next = list; + list = entry; + }; + + const auto append_atfork = + [](pthread_atfork_t*& list, pthread_atfork_t* entry) + { + while (list) + list = list->next; + entry->next = nullptr; + list = entry; + }; + + pthread_mutex_lock(&s_atfork_mutex); + + if (prepare_entry) + { + prepare_entry->function = prepare; + prepend_atfork(s_atfork_prepare, prepare_entry); + } + + if (parent_entry) + { + parent_entry->function = parent; + append_atfork(s_atfork_parent, parent_entry); + } + + if (child_entry) + { + child_entry->function = parent; + append_atfork(s_atfork_child, child_entry); + } + + pthread_mutex_unlock(&s_atfork_mutex); + + return 0; +} + static void pthread_cancel_handler(int) { uthread* uthread = _get_uthread(); diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 485a5235..71058053 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -423,7 +423,12 @@ int execvp(const char* pathname, char* const argv[]) pid_t fork(void) { - return syscall(SYS_FORK); + _pthread_call_atfork(_PTHREAD_ATFORK_PREPARE); + const pid_t pid = syscall(SYS_FORK); + if (pid == -1) + return -1; + _pthread_call_atfork(pid ? _PTHREAD_ATFORK_PARENT : _PTHREAD_ATFORK_CHILD); + return pid; } int pipe(int fildes[2])