diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt index 9905619d427..789e16d108a 100644 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ b/Userland/Libraries/LibC/CMakeLists.txt @@ -17,6 +17,7 @@ set(LIBC_SOURCES mntent.cpp netdb.cpp poll.cpp + pthread_integration.cpp pwd.cpp qsort.cpp scanf.cpp diff --git a/Userland/Libraries/LibC/bits/pthread_integration.h b/Userland/Libraries/LibC/bits/pthread_integration.h new file mode 100644 index 00000000000..5a7b5e24e6b --- /dev/null +++ b/Userland/Libraries/LibC/bits/pthread_integration.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +__BEGIN_DECLS + +void __pthread_fork_prepare(void); +void __pthread_fork_child(void); +void __pthread_fork_parent(void); +void __pthread_fork_atfork_register_prepare(void (*)(void)); +void __pthread_fork_atfork_register_parent(void (*)(void)); +void __pthread_fork_atfork_register_child(void (*)(void)); + +int __pthread_mutex_lock(void*); +int __pthread_mutex_unlock(void*); +int __pthread_mutex_init(void*, const void*); + +int __pthread_self(); + +#define __PTHREAD_MUTEX_NORMAL 0 +#define __PTHREAD_MUTEX_RECURSIVE 1 +#define __PTHREAD_MUTEX_INITIALIZER \ + { \ + 0, 0, 0, __PTHREAD_MUTEX_NORMAL \ + } + +__END_DECLS diff --git a/Userland/Libraries/LibC/pthread_integration.cpp b/Userland/Libraries/LibC/pthread_integration.cpp new file mode 100644 index 00000000000..37d39169beb --- /dev/null +++ b/Userland/Libraries/LibC/pthread_integration.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +// Most programs don't need this, no need to incur an extra mutex lock/unlock on them +static Atomic g_did_touch_atfork { false }; +static pthread_mutex_t g_atfork_list_mutex __PTHREAD_MUTEX_INITIALIZER; +static NeverDestroyed> g_atfork_prepare_list; +static NeverDestroyed> g_atfork_child_list; +static NeverDestroyed> g_atfork_parent_list; + +} + +extern "C" { +void __pthread_fork_prepare(void) +{ + if (!g_did_touch_atfork.load()) + return; + + __pthread_mutex_lock(&g_atfork_list_mutex); + for (auto entry : g_atfork_prepare_list.get()) + entry(); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +void __pthread_fork_child(void) +{ + if (!g_did_touch_atfork.load()) + return; + + __pthread_mutex_lock(&g_atfork_list_mutex); + for (auto entry : g_atfork_child_list.get()) + entry(); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +void __pthread_fork_parent(void) +{ + if (!g_did_touch_atfork.load()) + return; + + __pthread_mutex_lock(&g_atfork_list_mutex); + for (auto entry : g_atfork_parent_list.get()) + entry(); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +void __pthread_fork_atfork_register_prepare(void (*func)(void)) +{ + g_did_touch_atfork.store(true); + + __pthread_mutex_lock(&g_atfork_list_mutex); + g_atfork_prepare_list->append(func); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +void __pthread_fork_atfork_register_parent(void (*func)(void)) +{ + g_did_touch_atfork.store(true); + + __pthread_mutex_lock(&g_atfork_list_mutex); + g_atfork_parent_list->append(func); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +void __pthread_fork_atfork_register_child(void (*func)(void)) +{ + g_did_touch_atfork.store(true); + + __pthread_mutex_lock(&g_atfork_list_mutex); + g_atfork_child_list->append(func); + __pthread_mutex_unlock(&g_atfork_list_mutex); +} + +int __pthread_self() +{ + return gettid(); +} + +int __pthread_mutex_lock(void* mutexp) +{ + auto* mutex = reinterpret_cast(mutexp); + auto& atomic = reinterpret_cast&>(mutex->lock); + pthread_t this_thread = __pthread_self(); + for (;;) { + u32 expected = false; + if (!atomic.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) { + if (mutex->type == __PTHREAD_MUTEX_RECURSIVE && mutex->owner == this_thread) { + mutex->level++; + return 0; + } + sched_yield(); + continue; + } + mutex->owner = this_thread; + mutex->level = 0; + return 0; + } +} + +int __pthread_mutex_unlock(void* mutexp) +{ + auto* mutex = reinterpret_cast(mutexp); + if (mutex->type == __PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) { + mutex->level--; + return 0; + } + mutex->owner = 0; + mutex->lock = 0; + return 0; +} + +int __pthread_mutex_init(void* mutexp, const void* attrp) +{ + auto* mutex = reinterpret_cast(mutexp); + auto* attributes = reinterpret_cast(attrp); + mutex->lock = 0; + mutex->owner = 0; + mutex->level = 0; + mutex->type = attributes ? attributes->type : __PTHREAD_MUTEX_NORMAL; + return 0; +} +} diff --git a/Userland/Libraries/LibC/unistd.cpp b/Userland/Libraries/LibC/unistd.cpp index 40544d56f0c..5a7db9b413b 100644 --- a/Userland/Libraries/LibC/unistd.cpp +++ b/Userland/Libraries/LibC/unistd.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -75,10 +76,15 @@ int fchown(int fd, uid_t uid, gid_t gid) pid_t fork() { + __pthread_fork_prepare(); + int rc = syscall(SC_fork); if (rc == 0) { s_cached_tid = 0; s_cached_pid = 0; + __pthread_fork_child(); + } else if (rc != -1) { + __pthread_fork_parent(); } __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/Libraries/LibPthread/pthread.cpp b/Userland/Libraries/LibPthread/pthread.cpp index 9b327007f11..dbbe2aee4c1 100644 --- a/Userland/Libraries/LibPthread/pthread.cpp +++ b/Userland/Libraries/LibPthread/pthread.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -110,7 +111,7 @@ static int create_thread(pthread_t* thread, void* (*entry)(void*), void* argumen int pthread_self() { - return gettid(); + return __pthread_self(); } int pthread_create(pthread_t* thread, pthread_attr_t* attributes, void* (*start_routine)(void*), void* argument_to_start_routine) @@ -172,11 +173,7 @@ int pthread_sigmask(int how, const sigset_t* set, sigset_t* old_set) int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attributes) { - mutex->lock = 0; - mutex->owner = 0; - mutex->level = 0; - mutex->type = attributes ? attributes->type : PTHREAD_MUTEX_NORMAL; - return 0; + return __pthread_mutex_init(mutex, attributes); } int pthread_mutex_destroy(pthread_mutex_t*) @@ -186,22 +183,7 @@ int pthread_mutex_destroy(pthread_mutex_t*) int pthread_mutex_lock(pthread_mutex_t* mutex) { - auto& atomic = reinterpret_cast&>(mutex->lock); - pthread_t this_thread = pthread_self(); - for (;;) { - u32 expected = false; - if (!atomic.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) { - if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->owner == this_thread) { - mutex->level++; - return 0; - } - sched_yield(); - continue; - } - mutex->owner = this_thread; - mutex->level = 0; - return 0; - } + return __pthread_mutex_lock(mutex); } int pthread_mutex_trylock(pthread_mutex_t* mutex) @@ -222,13 +204,7 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) int pthread_mutex_unlock(pthread_mutex_t* mutex) { - if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) { - mutex->level--; - return 0; - } - mutex->owner = 0; - mutex->lock = 0; - return 0; + return __pthread_mutex_unlock(mutex); } int pthread_mutexattr_init(pthread_mutexattr_t* attr) @@ -922,9 +898,12 @@ int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int) ASSERT_NOT_REACHED(); } -int pthread_atfork(void (*)(void), void (*)(void), void (*)(void)) +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { - ASSERT_NOT_REACHED(); + __pthread_fork_atfork_register_prepare(prepare); + __pthread_fork_atfork_register_parent(parent); + __pthread_fork_atfork_register_child(child); + return 0; } } // extern "C" diff --git a/Userland/Libraries/LibPthread/pthread.h b/Userland/Libraries/LibPthread/pthread.h index e9fe3389d81..d801c859158 100644 --- a/Userland/Libraries/LibPthread/pthread.h +++ b/Userland/Libraries/LibPthread/pthread.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -75,13 +76,11 @@ int pthread_setspecific(pthread_key_t key, const void* value); int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param); int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param); -#define PTHREAD_MUTEX_NORMAL 0 -#define PTHREAD_MUTEX_RECURSIVE 1 +#define PTHREAD_MUTEX_NORMAL __PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_RECURSIVE __PTHREAD_MUTEX_RECURSIVE #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL -#define PTHREAD_MUTEX_INITIALIZER \ - { \ - 0, 0, 0, PTHREAD_MUTEX_DEFAULT \ - } +#define PTHREAD_MUTEX_INITIALIZER __PTHREAD_MUTEX_INITIALIZER + #define PTHREAD_COND_INITIALIZER \ { \ 0, 0, CLOCK_MONOTONIC_COARSE \