From 88cebb05ad67e306f51c74ff0f0ea1c83add3b39 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sat, 17 Apr 2021 18:38:32 +0200 Subject: [PATCH] LibC+LibPthread: Implement function forwarding for libpthread GCC will insert various calls to pthread functions when compiling C++ code with static initializers, even when the user doesn't link their program against libpthread explicitly. This is used to make static initializers thread-safe, e.g. when building a library that does not itself use thread functionality and thus does not link against libpthread - but is intended to be used with other code that does use libpthread explicitly. This makes these symbols available in libc. --- Userland/DynamicLoader/CMakeLists.txt | 3 +- Userland/Libraries/LibC/CMakeLists.txt | 2 + .../Libraries/LibC/bits/pthread_forward.h | 49 +++++++ .../Libraries/LibC/bits/pthread_integration.h | 16 ++- Userland/Libraries/LibC/crt0.cpp | 2 + Userland/Libraries/LibC/crt0_shared.cpp | 2 + Userland/Libraries/LibC/pthread_forward.cpp | 107 ++++++++++++++ .../Libraries/LibC/pthread_integration.cpp | 20 +-- Userland/Libraries/LibC/pthread_tls.cpp | 130 ++++++++++++++++++ Userland/Libraries/LibC/stdlib.cpp | 7 +- Userland/Libraries/LibPthread/CMakeLists.txt | 1 + Userland/Libraries/LibPthread/forward.cpp | 50 +++++++ Userland/Libraries/LibPthread/pthread.cpp | 90 ++---------- Userland/Libraries/LibPthread/pthread.h | 2 - 14 files changed, 381 insertions(+), 100 deletions(-) create mode 100644 Userland/Libraries/LibC/bits/pthread_forward.h create mode 100644 Userland/Libraries/LibC/pthread_forward.cpp create mode 100644 Userland/Libraries/LibC/pthread_tls.cpp create mode 100644 Userland/Libraries/LibPthread/forward.cpp diff --git a/Userland/DynamicLoader/CMakeLists.txt b/Userland/DynamicLoader/CMakeLists.txt index 85f93cf8556..3ce98360390 100644 --- a/Userland/DynamicLoader/CMakeLists.txt +++ b/Userland/DynamicLoader/CMakeLists.txt @@ -19,8 +19,7 @@ endif() file(GLOB LIBSYSTEM_SOURCES "../Libraries/LibSystem/*.cpp") -list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.cpp") -list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.+.cpp") +add_definitions(-D_DYNAMIC_LOADER) set(SOURCES ${LOADER_SOURCES} ${AK_SOURCES} ${ELF_SOURCES} ${LIBC_SOURCES1} ${LIBC_SOURCES2} ${LIBC_SOURCES3} ${LIBSYSTEM_SOURCES}) diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt index d13f6327cc6..f2d90963dbd 100644 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ b/Userland/Libraries/LibC/CMakeLists.txt @@ -20,7 +20,9 @@ set(LIBC_SOURCES mntent.cpp netdb.cpp poll.cpp + pthread_forward.cpp pthread_integration.cpp + pthread_tls.cpp pwd.cpp qsort.cpp scanf.cpp diff --git a/Userland/Libraries/LibC/bits/pthread_forward.h b/Userland/Libraries/LibC/bits/pthread_forward.h new file mode 100644 index 00000000000..d416a010227 --- /dev/null +++ b/Userland/Libraries/LibC/bits/pthread_forward.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Gunnar Beutner + * 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 + +struct PthreadFunctions { + int (*pthread_mutex_trylock)(pthread_mutex_t* mutex); + int (*pthread_mutex_destroy)(pthread_mutex_t*); + + int (*pthread_mutexattr_init)(pthread_mutexattr_t*); + int (*pthread_mutexattr_settype)(pthread_mutexattr_t*, int); + int (*pthread_mutexattr_destroy)(pthread_mutexattr_t*); + + int (*pthread_once)(pthread_once_t*, void (*)(void)); + + int (*pthread_cond_broadcast)(pthread_cond_t*); + int (*pthread_cond_init)(pthread_cond_t*, const pthread_condattr_t*); + int (*pthread_cond_signal)(pthread_cond_t*); + int (*pthread_cond_wait)(pthread_cond_t*, pthread_mutex_t*); + int (*pthread_cond_destroy)(pthread_cond_t*); + int (*pthread_cond_timedwait)(pthread_cond_t*, pthread_mutex_t*, const struct timespec*); +}; + +void __init_pthread_forward(PthreadFunctions); diff --git a/Userland/Libraries/LibC/bits/pthread_integration.h b/Userland/Libraries/LibC/bits/pthread_integration.h index 5a7b5e24e6b..842cfc9fb3a 100644 --- a/Userland/Libraries/LibC/bits/pthread_integration.h +++ b/Userland/Libraries/LibC/bits/pthread_integration.h @@ -27,6 +27,7 @@ #pragma once #include +#include __BEGIN_DECLS @@ -37,12 +38,21 @@ 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_mutex_lock(pthread_mutex_t*); +int __pthread_mutex_unlock(pthread_mutex_t*); +int __pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*); + +typedef void (*KeyDestructor)(void*); + +int __pthread_key_create(pthread_key_t*, KeyDestructor); +int __pthread_key_delete(pthread_key_t); +void* __pthread_getspecific(pthread_key_t); +int __pthread_setspecific(pthread_key_t, const void*); int __pthread_self(); +void __pthread_key_destroy_for_current_thread(); + #define __PTHREAD_MUTEX_NORMAL 0 #define __PTHREAD_MUTEX_RECURSIVE 1 #define __PTHREAD_MUTEX_INITIALIZER \ diff --git a/Userland/Libraries/LibC/crt0.cpp b/Userland/Libraries/LibC/crt0.cpp index 0988367fbb9..4fe1136e2d6 100644 --- a/Userland/Libraries/LibC/crt0.cpp +++ b/Userland/Libraries/LibC/crt0.cpp @@ -31,6 +31,7 @@ #include #include +#ifndef _DYNAMIC_LOADER extern "C" { extern u32 __stack_chk_guard; @@ -65,3 +66,4 @@ int _start(int argc, char** argv, char** env) return 20150614; } } +#endif diff --git a/Userland/Libraries/LibC/crt0_shared.cpp b/Userland/Libraries/LibC/crt0_shared.cpp index 8d18d674122..6ca6e20c7b8 100644 --- a/Userland/Libraries/LibC/crt0_shared.cpp +++ b/Userland/Libraries/LibC/crt0_shared.cpp @@ -31,4 +31,6 @@ #include #include +#ifndef _DYNAMIC_LOADER void* __dso_handle __attribute__((__weak__)); +#endif diff --git a/Userland/Libraries/LibC/pthread_forward.cpp b/Userland/Libraries/LibC/pthread_forward.cpp new file mode 100644 index 00000000000..1ff1eae9e5d --- /dev/null +++ b/Userland/Libraries/LibC/pthread_forward.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, Gunnar Beutner + * 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 + +static PthreadFunctions s_pthread_functions; + +void __init_pthread_forward(PthreadFunctions funcs) +{ + s_pthread_functions = funcs; +} + +int pthread_mutex_trylock(pthread_mutex_t* mutex) +{ + VERIFY(s_pthread_functions.pthread_mutex_trylock); + return s_pthread_functions.pthread_mutex_trylock(mutex); +} + +int pthread_mutex_destroy(pthread_mutex_t* mutex) +{ + VERIFY(s_pthread_functions.pthread_mutex_destroy); + return s_pthread_functions.pthread_mutex_destroy(mutex); +} + +int pthread_mutexattr_init(pthread_mutexattr_t* attr) +{ + VERIFY(s_pthread_functions.pthread_mutexattr_init); + return s_pthread_functions.pthread_mutexattr_init(attr); +} + +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) +{ + VERIFY(s_pthread_functions.pthread_mutexattr_settype); + return s_pthread_functions.pthread_mutexattr_settype(attr, type); +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) +{ + VERIFY(s_pthread_functions.pthread_mutexattr_destroy); + return s_pthread_functions.pthread_mutexattr_destroy(attr); +} + +int pthread_once(pthread_once_t* self, void (*callback)(void)) +{ + VERIFY(s_pthread_functions.pthread_once); + return s_pthread_functions.pthread_once(self, callback); +} + +int pthread_cond_broadcast(pthread_cond_t* cond) +{ + VERIFY(s_pthread_functions.pthread_cond_broadcast); + return s_pthread_functions.pthread_cond_broadcast(cond); +} + +int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) +{ + VERIFY(s_pthread_functions.pthread_cond_init); + return s_pthread_functions.pthread_cond_init(cond, attr); +} + +int pthread_cond_signal(pthread_cond_t* cond) +{ + VERIFY(s_pthread_functions.pthread_cond_signal); + return s_pthread_functions.pthread_cond_signal(cond); +} + +int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) +{ + VERIFY(s_pthread_functions.pthread_cond_wait); + return s_pthread_functions.pthread_cond_wait(cond, mutex); +} + +int pthread_cond_destroy(pthread_cond_t* cond) +{ + VERIFY(s_pthread_functions.pthread_cond_destroy); + return s_pthread_functions.pthread_cond_destroy(cond); +} + +int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime) +{ + VERIFY(s_pthread_functions.pthread_cond_timedwait); + return s_pthread_functions.pthread_cond_timedwait(cond, mutex, abstime); +} diff --git a/Userland/Libraries/LibC/pthread_integration.cpp b/Userland/Libraries/LibC/pthread_integration.cpp index 37d39169beb..4ccd1fae54f 100644 --- a/Userland/Libraries/LibC/pthread_integration.cpp +++ b/Userland/Libraries/LibC/pthread_integration.cpp @@ -26,11 +26,9 @@ #include #include -#include #include #include #include -#include #include namespace { @@ -110,9 +108,10 @@ int __pthread_self() return gettid(); } -int __pthread_mutex_lock(void* mutexp) +int pthread_self() __attribute__((weak, alias("__pthread_self"))); + +int __pthread_mutex_lock(pthread_mutex_t* mutex) { - auto* mutex = reinterpret_cast(mutexp); auto& atomic = reinterpret_cast&>(mutex->lock); pthread_t this_thread = __pthread_self(); for (;;) { @@ -131,9 +130,10 @@ int __pthread_mutex_lock(void* mutexp) } } -int __pthread_mutex_unlock(void* mutexp) +int pthread_mutex_lock(pthread_mutex_t*) __attribute__((weak, alias("__pthread_mutex_lock"))); + +int __pthread_mutex_unlock(pthread_mutex_t* mutex) { - auto* mutex = reinterpret_cast(mutexp); if (mutex->type == __PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) { mutex->level--; return 0; @@ -143,14 +143,16 @@ int __pthread_mutex_unlock(void* mutexp) return 0; } -int __pthread_mutex_init(void* mutexp, const void* attrp) +int pthread_mutex_unlock(pthread_mutex_t*) __attribute__((weak, alias("__pthread_mutex_unlock"))); + +int __pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attributes) { - 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; } + +int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*) __attribute__((weak, alias("__pthread_mutex_init"))); } diff --git a/Userland/Libraries/LibC/pthread_tls.cpp b/Userland/Libraries/LibC/pthread_tls.cpp new file mode 100644 index 00000000000..afa3cc0520e --- /dev/null +++ b/Userland/Libraries/LibC/pthread_tls.cpp @@ -0,0 +1,130 @@ +/* + * 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 + +#ifndef _DYNAMIC_LOADER +extern "C" { + +static constexpr int max_keys = PTHREAD_KEYS_MAX; + +struct KeyTable { + KeyDestructor destructors[max_keys] { nullptr }; + int next { 0 }; + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +}; + +struct SpecificTable { + void* values[max_keys] { nullptr }; +}; + +static KeyTable s_keys; + +__thread SpecificTable t_specifics; + +int __pthread_key_create(pthread_key_t* key, KeyDestructor destructor) +{ + int ret = 0; + __pthread_mutex_lock(&s_keys.mutex); + if (s_keys.next >= max_keys) { + ret = EAGAIN; + } else { + *key = s_keys.next++; + s_keys.destructors[*key] = destructor; + ret = 0; + } + __pthread_mutex_unlock(&s_keys.mutex); + return ret; +} + +int pthread_key_create(pthread_key_t*, KeyDestructor) __attribute__((weak, alias("__pthread_key_create"))); + +int __pthread_key_delete(pthread_key_t key) +{ + if (key < 0 || key >= max_keys) + return EINVAL; + __pthread_mutex_lock(&s_keys.mutex); + s_keys.destructors[key] = nullptr; + __pthread_mutex_unlock(&s_keys.mutex); + return 0; +} + +int pthread_key_delete(pthread_key_t) __attribute__((weak, alias("__pthread_key_delete"))); + +void* __pthread_getspecific(pthread_key_t key) +{ + if (key < 0) + return nullptr; + if (key >= max_keys) + return nullptr; + return t_specifics.values[key]; +} + +void* pthread_getspecific(pthread_key_t) __attribute__((weak, alias("__pthread_getspecific"))); + +int __pthread_setspecific(pthread_key_t key, const void* value) +{ + if (key < 0) + return EINVAL; + if (key >= max_keys) + return EINVAL; + + t_specifics.values[key] = const_cast(value); + return 0; +} + +int pthread_setspecific(pthread_key_t, const void*) __attribute__((weak, alias("__pthread_setspecific"))); + +void __pthread_key_destroy_for_current_thread() +{ + // This function will either be called during exit_thread, for a pthread, or + // during global program shutdown for the main thread. + + __pthread_mutex_lock(&s_keys.mutex); + size_t num_used_keys = s_keys.next; + + // Dr. POSIX accounts for weird key destructors setting their own key again. + // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes. + + for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) { + bool any_nonnull_destructors = false; + for (size_t key_index = 0; key_index < num_used_keys; ++key_index) { + void* value = exchange(t_specifics.values[key_index], nullptr); + + if (value && s_keys.destructors[key_index]) { + any_nonnull_destructors = true; + (*s_keys.destructors[key_index])(value); + } + } + if (!any_nonnull_destructors) + break; + } + __pthread_mutex_unlock(&s_keys.mutex); +} +} +#endif diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp index 202352d0030..00b5903889c 100644 --- a/Userland/Libraries/LibC/stdlib.cpp +++ b/Userland/Libraries/LibC/stdlib.cpp @@ -49,8 +49,6 @@ #include #include -void (*__libc_pthread_key_destroy_for_current_thread)() = nullptr; - static void strtons(const char* str, char** endptr) { assert(endptr); @@ -228,8 +226,9 @@ void exit(int status) fflush(stdout); fflush(stderr); - if (__libc_pthread_key_destroy_for_current_thread) - __libc_pthread_key_destroy_for_current_thread(); +#ifndef _DYNAMIC_LOADER + __pthread_key_destroy_for_current_thread(); +#endif _exit(status); } diff --git a/Userland/Libraries/LibPthread/CMakeLists.txt b/Userland/Libraries/LibPthread/CMakeLists.txt index 0c29d56e60f..74896b49761 100644 --- a/Userland/Libraries/LibPthread/CMakeLists.txt +++ b/Userland/Libraries/LibPthread/CMakeLists.txt @@ -1,4 +1,5 @@ set(SOURCES + forward.cpp pthread.cpp pthread_once.cpp semaphore.cpp diff --git a/Userland/Libraries/LibPthread/forward.cpp b/Userland/Libraries/LibPthread/forward.cpp new file mode 100644 index 00000000000..aba8bfcf6a3 --- /dev/null +++ b/Userland/Libraries/LibPthread/forward.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Gunnar Beutner + * 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 + +static const PthreadFunctions s_functions = { + .pthread_mutex_trylock = pthread_mutex_trylock, + .pthread_mutex_destroy = pthread_mutex_destroy, + + .pthread_mutexattr_init = pthread_mutexattr_init, + .pthread_mutexattr_settype = pthread_mutexattr_settype, + .pthread_mutexattr_destroy = pthread_mutexattr_destroy, + + .pthread_once = pthread_once, + + .pthread_cond_broadcast = pthread_cond_broadcast, + .pthread_cond_init = pthread_cond_init, + .pthread_cond_signal = pthread_cond_signal, + .pthread_cond_wait = pthread_cond_wait, + .pthread_cond_destroy = pthread_cond_destroy, + .pthread_cond_timedwait = pthread_cond_timedwait, +}; + +[[gnu::constructor]] static void forward_pthread_functions() +{ + __init_pthread_forward(s_functions); +} diff --git a/Userland/Libraries/LibPthread/pthread.cpp b/Userland/Libraries/LibPthread/pthread.cpp index 4372f2bf7d2..1d5ca118d73 100644 --- a/Userland/Libraries/LibPthread/pthread.cpp +++ b/Userland/Libraries/LibPthread/pthread.cpp @@ -55,8 +55,6 @@ constexpr size_t highest_reasonable_stack_size = 8 * MiB; // That's the default #define __RETURN_PTHREAD_ERROR(rc) \ return ((rc) < 0 ? -(rc) : 0) -extern void (*__libc_pthread_key_destroy_for_current_thread)(); - extern "C" { static void* pthread_create_helper(void* (*routine)(void*), void* argument) @@ -531,99 +529,31 @@ int pthread_cond_broadcast(pthread_cond_t* cond) return 0; } -static constexpr int max_keys = PTHREAD_KEYS_MAX; - -typedef void (*KeyDestructor)(void*); - -struct KeyTable { - KeyDestructor destructors[max_keys] { nullptr }; - int next { 0 }; - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -}; - -struct SpecificTable { - void* values[max_keys] { nullptr }; -}; - -static KeyTable s_keys; - -__thread SpecificTable t_specifics; +// libgcc expects this function to exist in libpthread, even +// if it is not implemented. +int pthread_cancel(pthread_t) +{ + TODO(); +} int pthread_key_create(pthread_key_t* key, KeyDestructor destructor) { - int ret = 0; - pthread_mutex_lock(&s_keys.mutex); - if (s_keys.next >= max_keys) { - ret = EAGAIN; - } else { - *key = s_keys.next++; - s_keys.destructors[*key] = destructor; - ret = 0; - } - pthread_mutex_unlock(&s_keys.mutex); - return ret; + return __pthread_key_create(key, destructor); } int pthread_key_delete(pthread_key_t key) { - if (key < 0 || key >= max_keys) - return EINVAL; - pthread_mutex_lock(&s_keys.mutex); - s_keys.destructors[key] = nullptr; - pthread_mutex_unlock(&s_keys.mutex); - return 0; + return __pthread_key_delete(key); } void* pthread_getspecific(pthread_key_t key) { - if (key < 0) - return nullptr; - if (key >= max_keys) - return nullptr; - return t_specifics.values[key]; + return __pthread_getspecific(key); } int pthread_setspecific(pthread_key_t key, const void* value) { - if (key < 0) - return EINVAL; - if (key >= max_keys) - return EINVAL; - - t_specifics.values[key] = const_cast(value); - return 0; -} - -[[gnu::constructor]] static void set_libc_key_destructor() -{ - __libc_pthread_key_destroy_for_current_thread = __pthread_key_destroy_for_current_thread; -} - -void __pthread_key_destroy_for_current_thread() -{ - // This function will either be called during exit_thread, for a pthread, or - // during global program shutdown for the main thread. - - pthread_mutex_lock(&s_keys.mutex); - size_t num_used_keys = s_keys.next; - - // Dr. POSIX accounts for weird key destructors setting their own key again. - // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes. - - for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) { - bool any_nonnull_destructors = false; - for (size_t key_index = 0; key_index < num_used_keys; ++key_index) { - void* value = exchange(t_specifics.values[key_index], nullptr); - - if (value && s_keys.destructors[key_index]) { - any_nonnull_destructors = true; - (*s_keys.destructors[key_index])(value); - } - } - if (!any_nonnull_destructors) - break; - } - pthread_mutex_unlock(&s_keys.mutex); + return __pthread_setspecific(key, value); } int pthread_setname_np(pthread_t thread, const char* name) diff --git a/Userland/Libraries/LibPthread/pthread.h b/Userland/Libraries/LibPthread/pthread.h index 4e470c1cefa..d801c859158 100644 --- a/Userland/Libraries/LibPthread/pthread.h +++ b/Userland/Libraries/LibPthread/pthread.h @@ -146,6 +146,4 @@ int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int); int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); -void __pthread_key_destroy_for_current_thread(); - __END_DECLS