LibC: Implement pthread_atfork

Again this code is not tested but *feels* right :D
This commit is contained in:
Bananymous 2025-06-01 01:41:09 +03:00
parent dbdefa0f4a
commit df7f245cf8
3 changed files with 107 additions and 1 deletions

View File

@ -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);

View File

@ -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<pthread_atfork_t*>(malloc(sizeof(pthread_attr_t)));
pthread_atfork_t* parent_entry = nullptr;
if (parent != nullptr)
parent_entry = static_cast<pthread_atfork_t*>(malloc(sizeof(pthread_attr_t)));
pthread_atfork_t* child_entry = nullptr;
if (child != nullptr)
child_entry = static_cast<pthread_atfork_t*>(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();

View File

@ -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])