Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -126,7 +126,10 @@ "Build libc++ with support for a monotonic clock. This option may only be set to OFF when LIBCXX_ENABLE_THREADS=OFF." ON) option(LIBCXX_HAS_MUSL_LIBC "Build libc++ with support for the Musl C library" OFF) -option(LIBCXX_HAS_PTHREAD_API "Ignore auto-detection and force use of pthread API" OFF) +option(LIBCXX_HAS_PTHREAD_THREAD_API "Ignore auto-detection and force use of pthread API" OFF) +option(LIBCXX_HAS_EXTERNAL_THREAD_API + "Build libc++ with an externalized threading API. + This option may only be set to ON when LIBCXX_ENABLE_THREADS=ON." OFF) # Misc options ---------------------------------------------------------------- # FIXME: Turn -pedantic back ON. It is currently off because it warns @@ -166,15 +169,20 @@ # Check option configurations #=============================================================================== -# Ensure LIBCXX_ENABLE_MONOTONIC_CLOCK is set to ON only when +# Ensure LIBCXX_ENABLE_MONOTONIC_CLOCK is always set to ON when # LIBCXX_ENABLE_THREADS is on. if(LIBCXX_ENABLE_THREADS AND NOT LIBCXX_ENABLE_MONOTONIC_CLOCK) message(FATAL_ERROR "LIBCXX_ENABLE_MONOTONIC_CLOCK can only be set to OFF" " when LIBCXX_ENABLE_THREADS is also set to OFF.") endif() -if(LIBCXX_HAS_PTHREAD_API AND NOT LIBCXX_ENABLE_THREADS) - message(FATAL_ERROR "LIBCXX_HAS_PTHREAD_API can only be set to ON" +if(LIBCXX_HAS_PTHREAD_THREAD_API AND NOT LIBCXX_ENABLE_THREADS) + message(FATAL_ERROR "LIBCXX_HAS_PTHREAD_THREAD_API can only be set to ON" + " when LIBCXX_ENABLE_THREADS is also set to ON.") +endif() + +if(LIBCXX_HAS_EXTERNAL_THREAD_API AND NOT LIBCXX_ENABLE_THREADS) + message(FATAL_ERROR "LIBCXX_HAS_EXTERNAL_THREAD_API can only be set to ON" " when LIBCXX_ENABLE_THREADS is also set to ON.") endif() @@ -394,7 +402,8 @@ config_define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK _LIBCPP_HAS_NO_MONOTONIC_CLOCK) config_define_if_not(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS) -config_define_if(LIBCXX_HAS_PTHREAD_API _LIBCPP_HAS_THREAD_API_PTHREAD) +config_define_if(LIBCXX_HAS_PTHREAD_THREAD_API _LIBCPP_HAS_THREAD_API_PTHREAD) +config_define_if(LIBCXX_HAS_EXTERNAL_THREAD_API _LIBCPP_HAS_THREAD_API_EXTERNAL) config_define_if(LIBCXX_HAS_MUSL_LIBC _LIBCPP_HAS_MUSL_LIBC) if (LIBCXX_NEEDS_SITE_CONFIG) Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -813,7 +813,9 @@ #endif // Thread API -#if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +#if !defined(_LIBCPP_HAS_NO_THREADS) && \ + !defined(_LIBCPP_HAS_THREAD_API_PTHREAD) && \ + !defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) # if defined(__FreeBSD__) || \ defined(__NetBSD__) || \ defined(__linux__) || \ @@ -831,6 +833,11 @@ _LIBCPP_HAS_NO_THREADS is not defined. #endif +#if defined(_LIBCPP_HAS_NO_THREADS) && defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +# error _LIBCPP_HAS_EXTERNAL_THREAD_API may not be defined when \ + _LIBCPP_HAS_NO_THREADS is defined. +#endif + #if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK) && !defined(_LIBCPP_HAS_NO_THREADS) # error _LIBCPP_HAS_NO_MONOTONIC_CLOCK may only be defined when \ _LIBCPP_HAS_NO_THREADS is defined. Index: include/__config_site.in =================================================================== --- include/__config_site.in +++ include/__config_site.in @@ -20,5 +20,6 @@ #cmakedefine _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS #cmakedefine _LIBCPP_HAS_MUSL_LIBC #cmakedefine _LIBCPP_HAS_THREAD_API_PTHREAD +#cmakedefine _LIBCPP_HAS_THREAD_API_EXTERNAL #endif // _LIBCPP_CONFIG_SITE Index: include/__dynamic_threading =================================================================== --- /dev/null +++ include/__dynamic_threading @@ -0,0 +1,76 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_DYNAMIC_THREADING +#define _LIBCPP_DYNAMIC_THREADING + +#include <__config> + +#ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +#pragma GCC system_header +#endif + +#if !defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +# error "Only valid when _LIBCPP_HAS_THREAD_API_EXTERNAL is defined" +#endif + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +// Mutex +#define _LIBCPP_MUTEX_INITIALIZER nullptr +struct __libcpp_platform_mutex_t; +typedef __libcpp_platform_mutex_t *__libcpp_mutex_t; + +int __libcpp_recursive_mutex_init(__libcpp_mutex_t* __m); +int __libcpp_mutex_lock(__libcpp_mutex_t* __m); +int __libcpp_mutex_trylock(__libcpp_mutex_t* __m); +int __libcpp_mutex_unlock(__libcpp_mutex_t* __m); +int __libcpp_mutex_destroy(__libcpp_mutex_t* __m); + +// Condition variable +#define _LIBCPP_CONDVAR_INITIALIZER nullptr +struct __libcpp_platform_condvar_t; +typedef __libcpp_platform_condvar_t *__libcpp_condvar_t; + +int __libcpp_condvar_signal(__libcpp_condvar_t* __cv); +int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv); +int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m); +int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m, timespec* __ts); +int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv); + +// Thread id +typedef unsigned long __libcpp_thread_id; +bool __libcpp_thread_id_equal(__libcpp_thread_id t1, __libcpp_thread_id t2); +bool __libcpp_thread_id_less(__libcpp_thread_id t1, __libcpp_thread_id t2); + +// Thread +struct __libcpp_platform_thread_t; +typedef __libcpp_platform_thread_t *__libcpp_thread_t; +int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg); +__libcpp_thread_id __libcpp_thread_get_current_id(); +__libcpp_thread_id __libcpp_thread_get_id(const __libcpp_thread_t* __t); +int __libcpp_thread_join(__libcpp_thread_t* __t); +int __libcpp_thread_detach(__libcpp_thread_t* __t); +void __libcpp_thread_yield(); +void __libcpp_thread_finalize(__libcpp_thread_t* __t); + +// Thread local storage +struct __libcpp_platform_tlskey_t; +typedef __libcpp_platform_tlskey_t *__libcpp_tl_key; +int __libcpp_tl_create(__libcpp_tl_key* __key, void (*__at_exit)(void*)); +void* __libcpp_tl_get(__libcpp_tl_key __key); +void __libcpp_tl_set(__libcpp_tl_key __key, void* __p); +void __libcpp_tl_finalize(__libcpp_tl_key __key); + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_DYNAMIC_THREADING Index: include/__threading_support =================================================================== --- include/__threading_support +++ include/__threading_support @@ -24,10 +24,10 @@ #include #endif -_LIBCPP_BEGIN_NAMESPACE_STD - #if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +_LIBCPP_BEGIN_NAMESPACE_STD + // Mutex #define _LIBCPP_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER typedef pthread_mutex_t __libcpp_mutex_t; @@ -194,12 +194,26 @@ pthread_setspecific(__key, __p); } -#else // !_LIBCPP_HAS_THREAD_API_PTHREAD +_LIBCPP_END_NAMESPACE_STD + +#elif defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +/* !!!FOR LIBRARY IMPLEMENTORS ONLY!!! */ +# if !defined(__has_include) || __has_include(<__static_threading>) + /* For compiling libc++ against an external thread implementation. + This header should provide the definitions of various __libcpp_* + functions declared above. These functions should be defined as + _LIBCPP_ALWAYS_INLINE so that they are inlined into the library + code. */ +# include<__static_threading> +# else + /* For compiling libc++ with an externalized (runtime) thread API. This + is the default when no __static_threading header is provided. */ +# include<__dynamic_threading> +# endif +#else #error "No thread API selected." #endif -_LIBCPP_END_NAMESPACE_STD - #endif // _LIBCPP_HAS_NO_THREADS #endif // _LIBCPP_THREADING_SUPPORT Index: include/thread =================================================================== --- include/thread +++ include/thread @@ -188,6 +188,10 @@ // so this destructor is only invoked during program termination. Invoking // pthread_key_delete(__key_) may prevent other threads from deleting their // thread local data. For this reason we leak the key. + +#if defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) + __libcpp_tl_finalize(__key_); +#endif } template Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -155,6 +155,18 @@ ) endif() +if (LIBCXX_HAS_EXTERNAL_THREAD_API) + file(GLOB LIBCXX_EXTERNAL_THREADING_SUPPORT_SOURCES ../test/support/external_threads.cpp) + add_library(cxx_external_threading_support STATIC ${LIBCXX_EXTERNAL_THREADING_SUPPORT_SOURCES}) + target_link_libraries(cxx_external_threading_support cxx) + + set_target_properties(cxx_external_threading_support + PROPERTIES + COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}" + OUTPUT_NAME "c++external_threading_support" + ) +endif() + # Generate a linker script inplace of a libc++.so symlink. Rerun this command # after cxx builds. if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT) Index: src/algorithm.cpp =================================================================== --- src/algorithm.cpp +++ src/algorithm.cpp @@ -48,14 +48,22 @@ template unsigned __sort5<__less&, long double*>(long double*, long double*, long double*, long double*, long double*, __less&); #ifndef _LIBCPP_HAS_NO_THREADS +# if defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +static mutex __rs_mut; +# define LOCK_LOCK() __rs_mut.lock(); +# define LOCK_UNLOCK() __rs_mut.unlock(); +# else static __libcpp_mutex_t __rs_mut = _LIBCPP_MUTEX_INITIALIZER; +# define LOCK_LOCK() __libcpp_mutex_lock(&__rs_mut); +# define LOCK_UNLOCK() __libcpp_mutex_unlock(&__rs_mut); +# endif #endif unsigned __rs_default::__c_ = 0; __rs_default::__rs_default() { #ifndef _LIBCPP_HAS_NO_THREADS - __libcpp_mutex_lock(&__rs_mut); + LOCK_LOCK() #endif __c_ = 1; } @@ -69,7 +77,7 @@ { #ifndef _LIBCPP_HAS_NO_THREADS if (--__c_ == 0) - __libcpp_mutex_unlock(&__rs_mut); + LOCK_UNLOCK() #else --__c_; #endif Index: src/memory.cpp =================================================================== --- src/memory.cpp +++ src/memory.cpp @@ -127,6 +127,9 @@ #if defined(_LIBCPP_HAS_C_ATOMIC_IMP) && !defined(_LIBCPP_HAS_NO_THREADS) static const std::size_t __sp_mut_count = 16; +#if defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +static mutex mut_back_imp[__sp_mut_count]; +#else static __libcpp_mutex_t mut_back_imp[__sp_mut_count] = { _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, @@ -134,6 +137,7 @@ _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER }; +#endif static mutex* mut_back = reinterpret_cast(mut_back_imp); Index: src/mutex.cpp =================================================================== --- src/mutex.cpp +++ src/mutex.cpp @@ -195,8 +195,23 @@ // keep in sync with: 7741191. #ifndef _LIBCPP_HAS_NO_THREADS +# if defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) +static mutex mut; +static condition_variable cv; +# define LOCK_INIT() unique_lock lk(mut); +# define LOCK_LOCK() lk.lock(); +# define LOCK_UNLOCK() lk.unlock(); +# define CV_WAIT_LOCK() cv.wait(lk); +# define CV_NOTIFY_ALL() cv.notify_all(); +# else static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER; static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER; +# define LOCK_INIT() __libcpp_mutex_lock(&mut); +# define LOCK_LOCK() __libcpp_mutex_lock(&mut); +# define LOCK_UNLOCK() __libcpp_mutex_unlock(&mut); +# define CV_WAIT_LOCK() __libcpp_condvar_wait(&cv, &mut); +# define CV_NOTIFY_ALL() __libcpp_condvar_broadcast(&cv); +# endif #endif /// NOTE: Changes to flag are done via relaxed atomic stores @@ -225,9 +240,9 @@ #endif // _LIBCPP_NO_EXCEPTIONS } #else // !_LIBCPP_HAS_NO_THREADS - __libcpp_mutex_lock(&mut); + LOCK_INIT() while (flag == 1) - __libcpp_condvar_wait(&cv, &mut); + CV_WAIT_LOCK() if (flag == 0) { #ifndef _LIBCPP_NO_EXCEPTIONS @@ -235,26 +250,26 @@ { #endif // _LIBCPP_NO_EXCEPTIONS __libcpp_relaxed_store(&flag, 1ul); - __libcpp_mutex_unlock(&mut); + LOCK_UNLOCK() func(arg); - __libcpp_mutex_lock(&mut); + LOCK_LOCK() __libcpp_relaxed_store(&flag, ~0ul); - __libcpp_mutex_unlock(&mut); - __libcpp_condvar_broadcast(&cv); + LOCK_UNLOCK() + CV_NOTIFY_ALL() #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { - __libcpp_mutex_lock(&mut); + LOCK_LOCK() __libcpp_relaxed_store(&flag, 0ul); - __libcpp_mutex_unlock(&mut); - __libcpp_condvar_broadcast(&cv); + LOCK_UNLOCK() + CV_NOTIFY_ALL() throw; } #endif // _LIBCPP_NO_EXCEPTIONS } else - __libcpp_mutex_unlock(&mut); + LOCK_UNLOCK() #endif // !_LIBCPP_HAS_NO_THREADS } Index: src/thread.cpp =================================================================== --- src/thread.cpp +++ src/thread.cpp @@ -39,6 +39,9 @@ thread::~thread() { +#if defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) + __libcpp_thread_finalize(&__t_); +#endif if (__t_ != 0) terminate(); } Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -18,6 +18,7 @@ pythonize_bool(LIBCXXABI_ENABLE_SHARED) pythonize_bool(LIBCXXABI_USE_LLVM_UNWINDER) pythonize_bool(LIBCXX_HAS_ATOMIC_LIB) +pythonize_bool(LIBCXX_HAS_EXTERNAL_THREAD_API) # The tests shouldn't link to any ABI library when it has been linked into # libc++ statically or via a linker script. @@ -48,10 +49,14 @@ set(experimental_dep cxx_experimental) endif() +if (LIBCXX_HAS_EXTERNAL_THREAD_API) + set(external_threading_support_dep cxx_external_threading_support) +endif() + add_lit_testsuite(check-libcxx "Running libcxx tests" ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS cxx ${experimental_dep}) + DEPENDS cxx ${experimental_dep} ${external_threading_support_dep}) if (LIBCXX_GENERATE_COVERAGE) include(CodeCoverage) Index: test/libcxx/test/config.py =================================================================== --- test/libcxx/test/config.py +++ test/libcxx/test/config.py @@ -473,6 +473,10 @@ self.cxx.link_flags += [abs_path] else: self.cxx.link_flags += ['-lc++'] + # This needs to come after -lc++ as we want its unresolved thread-api symbols + # to be picked up from this one. + if self.get_lit_bool('libcxx_external_thread_api', default=False): + self.cxx.link_flags += ['-lc++external_threading_support'] def configure_link_flags_abi_library(self): cxx_abi = self.get_lit_conf('cxx_abi', 'libcxxabi') Index: test/lit.site.cfg.in =================================================================== --- test/lit.site.cfg.in +++ test/lit.site.cfg.in @@ -23,6 +23,7 @@ config.llvm_unwinder = "@LIBCXXABI_USE_LLVM_UNWINDER@" config.use_libatomic = "@LIBCXX_HAS_ATOMIC_LIB@" config.libcxxabi_shared = "@LIBCXXABI_ENABLE_SHARED@" +config.libcxx_external_thread_api = "@LIBCXX_HAS_EXTERNAL_THREAD_API@" # Let the main config do the real work. lit_config.load_config(config, "@LIBCXX_SOURCE_DIR@/test/lit.cfg") Index: test/std/thread/thread.condition/thread.condition.condvar/native_handle.pass.cpp =================================================================== --- test/std/thread/thread.condition/thread.condition.condvar/native_handle.pass.cpp +++ test/std/thread/thread.condition/thread.condition.condvar/native_handle.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: libcpp-has-no-threads, libcpp-has-thread-api-external // Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.class/native_handle.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.class/native_handle.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.class/native_handle.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: libcpp-has-no-threads, libcpp-has-thread-api-external // Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.recursive/native_handle.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.recursive/native_handle.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.mutex.requirements.mutex/thread.mutex.recursive/native_handle.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: libcpp-has-no-threads, libcpp-has-thread-api-external // Index: test/std/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp =================================================================== --- test/std/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp +++ test/std/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: libcpp-has-no-threads, libcpp-has-thread-api-external // Index: test/std/thread/thread.threads/thread.thread.class/types.pass.cpp =================================================================== --- test/std/thread/thread.threads/thread.thread.class/types.pass.cpp +++ test/std/thread/thread.threads/thread.thread.class/types.pass.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: libcpp-has-no-threads +// UNSUPPORTED: libcpp-has-no-threads, libcpp-has-thread-api-external // Index: test/support/external_threads.cpp =================================================================== --- /dev/null +++ test/support/external_threads.cpp @@ -0,0 +1,340 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef SUPPORT_EXTERNAL_THREADS +#define SUPPORT_EXTERNAL_THREADS + +#include <__threading_support> + +// Only define these symbols if using the __dynamic_threading header +#if defined(_LIBCPP_DYNAMIC_THREADING) + +#include +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +/* In the pthread-based (or statically-threaded) libc++ variant, pthread + primitives (like pthread_mutex_t) become part of the internal states of the + corresponding C++ constructs (e.g. C++ mutex class holds an internal + pthread_mutex_t object). In contrast, in the externally-threaded libc++ + variant, C++ constructs hold (opaque) pointers to the underlying platform- + defined threading primitives. The following macros are used to convert the + opaque pointer types held within the C++ constructs and cast them as pointers + to the appropriate platform-defined thread primitive type (in this case, + pthread types again). +*/ +#define AS_PTHREAD_MUTPTR(x) reinterpret_cast(x) +#define AS_PTHREAD_CONDPTR(x) reinterpret_cast(x) +#define AS_PTHREAD_TPTR(x) reinterpret_cast(x) +#define AS_PTHREAD_KPTR(x) reinterpret_cast(x) + +//-- Mutex --// + +/* This method is invoked from within the std::recursive_mutex constructor, as + such, it does not need to be thread-safe when initializing the *__m pointer. +*/ +int __libcpp_recursive_mutex_init(__libcpp_mutex_t* __m) +{ + // Populate the internal opaque pointer *__m + pthread_mutex_t *mut = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t)); + if (mut == nullptr) + return -1; + *__m = reinterpret_cast<__libcpp_mutex_t> (mut); + + // Initialize the allocated pthread_mutex_t object as a recursive mutex + pthread_mutexattr_t attr; + int __ec = pthread_mutexattr_init(&attr); + if (__ec) + goto fail; + + __ec = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (__ec) + { + pthread_mutexattr_destroy(&attr); + goto fail; + } + + __ec = pthread_mutex_init(AS_PTHREAD_MUTPTR(*__m), &attr); + if (__ec) + { + pthread_mutexattr_destroy(&attr); + goto fail; + } + + __ec = pthread_mutexattr_destroy(&attr); + if (__ec) + { + pthread_mutex_destroy(AS_PTHREAD_MUTPTR(*__m)); + goto fail; + } + + return 0; + +fail: + free(*__m); + return __ec; +} + +/* C++11 standard requires mutex and condition_variable constructors to be + constexpr qualifying. This prohibits any prospects of calling a runtime + initialization routine from within mutex / condition_varialbe constructors. + For this reason, the external thread API must adopt an initialize-on-first- + use policy for mutexes and condition variables. With this strategy, we need + to be especially careful when initializing the internal opaque pointer *__m, + as multiple threads could attempt to lock the same mutex at once. In the + following routine, I've used a plain pthread mutex to make this initialization + thread-safe. It's also possible to use atomic compare-exchange builtins (where + available) to achieve the same goal. +*/ +pthread_mutex_t mutex_init_access = PTHREAD_MUTEX_INITIALIZER; + +int mutex_safe_init(volatile __libcpp_mutex_t* __m) { + pthread_mutex_lock(&mutex_init_access); + if (*__m == nullptr) { + pthread_mutex_t *mut = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t)); + if (mut == nullptr) + return -1; + if (pthread_mutex_init(mut, nullptr)) { + free(mut); + return -1; + } + + *__m = reinterpret_cast<__libcpp_mutex_t> (mut); + } + pthread_mutex_unlock(&mutex_init_access); + + return 0; +} + +int __libcpp_mutex_lock(__libcpp_mutex_t* __m) +{ + // initialize-on-first-use + if (*__m == nullptr && mutex_safe_init(__m)) + return -1; + + return pthread_mutex_lock(AS_PTHREAD_MUTPTR(*__m)); +} + +int __libcpp_mutex_trylock(__libcpp_mutex_t* __m) +{ + // initialize-on-first-use + if (*__m == nullptr && mutex_safe_init(__m)) + return -1; + + return pthread_mutex_trylock(AS_PTHREAD_MUTPTR(*__m)); +} + +int __libcpp_mutex_unlock(__libcpp_mutex_t* __m) +{ + if (*__m == nullptr) + return 0; + + return pthread_mutex_unlock(AS_PTHREAD_MUTPTR(*__m)); +} + +int __libcpp_mutex_destroy(__libcpp_mutex_t* __m) { + if (*__m == nullptr) + return 0; + + int ec = pthread_mutex_destroy(AS_PTHREAD_MUTPTR(*__m)); + free(*__m); + return ec; +} + +//-- Condition variable --// + +int __libcpp_condvar_signal(__libcpp_condvar_t* __cv) +{ + if (*__cv == nullptr) + return 0; + + return pthread_cond_signal(AS_PTHREAD_CONDPTR(*__cv)); +} + +int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv) +{ + if (*__cv == nullptr) + return 0; + + return pthread_cond_broadcast(AS_PTHREAD_CONDPTR(*__cv)); +} + +// See comment for mutex_safe_init() +pthread_mutex_t condvar_init_access = PTHREAD_MUTEX_INITIALIZER; + +int condvar_safe_init(__libcpp_condvar_t* __cv) { + pthread_mutex_lock(&condvar_init_access); + if (*__cv == nullptr) { + pthread_cond_t *cv = (pthread_cond_t*) malloc(sizeof(pthread_cond_t)); + if (cv == nullptr) + return -1; + if (pthread_cond_init(cv, nullptr)) { + free(cv); + return -1; + } + + *__cv = reinterpret_cast<__libcpp_condvar_t> (cv); + } + pthread_mutex_unlock(&condvar_init_access); + + return 0; +} + +int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m) +{ + // initialize-on-first-use + if (*__cv == nullptr && condvar_safe_init(__cv)) + return -1; + + return pthread_cond_wait(AS_PTHREAD_CONDPTR(*__cv), AS_PTHREAD_MUTPTR(*__m)); +} + +int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m, timespec* __ts) +{ + // initialize-on-first-use + if (*__cv == nullptr && condvar_safe_init(__cv)) + return -1; + + return pthread_cond_timedwait(AS_PTHREAD_CONDPTR(*__cv), AS_PTHREAD_MUTPTR(*__m), __ts); +} + +int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv) +{ + if (*__cv == nullptr) + return 0; + + int ec = pthread_cond_destroy(AS_PTHREAD_CONDPTR(*__cv)); + free(*__cv); + return ec; +} + +//-- Thread --// + +bool __libcpp_thread_id_equal(__libcpp_thread_id t1, __libcpp_thread_id t2) { + return pthread_equal(t1, t2) != 0; +} + +bool __libcpp_thread_id_less(__libcpp_thread_id t1, __libcpp_thread_id t2) { + return t1 < t2; +} + +/* This method is invoked from within the std::thread constructor, as such, it + does not need to be thread-safe when initializing the *__t pointer. +*/ +int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg) +{ + pthread_t *td = (pthread_t*) malloc(sizeof(pthread_t)); + if (td == nullptr) + return -1; + + *__t = reinterpret_cast<__libcpp_thread_t>(td); + + return pthread_create(AS_PTHREAD_TPTR(*__t), 0, __func, __arg); +} + +__libcpp_thread_id __libcpp_thread_get_current_id() +{ + return static_cast<__libcpp_thread_id>(pthread_self()); +} + +__libcpp_thread_id __libcpp_thread_get_id(const __libcpp_thread_t* __t) +{ + if (*__t == 0) + return 0; + + return static_cast<__libcpp_thread_id>(*AS_PTHREAD_TPTR(*__t)); +} + +int __libcpp_thread_join(__libcpp_thread_t* __t) +{ + // Must return non-zero if the internal state has already been invalidated + // This can happen for example, if std::thread::join() has been called + // before. + if (*__t == 0) + return -1; + + // Must cleanup memory allocated in __libcpp_thread_create() here, as the + // library will set *__t to zero after this call. + int ec = pthread_join(*AS_PTHREAD_TPTR(*__t), 0); + free(*__t); + return ec; +} + +int __libcpp_thread_detach(__libcpp_thread_t* __t) +{ + // Must return non-zero if the internal state has already been invalidated + // This can happen for example, if std::thread::detach() has been called + // before. + if (*__t == 0) + return -1; + + // Must cleanup memory allocated in __libcpp_thread_create() here, as the + // library will set *__t to zero after this call. + int ec = pthread_detach(*AS_PTHREAD_TPTR(*__t)); + free(*__t); + return ec; +} + +void __libcpp_thread_yield() +{ + sched_yield(); +} + +void __libcpp_thread_finalize(__libcpp_thread_t* __t) { + // Cleanup memory allocated in __libcpp_thread_create() + if (*__t != 0) + free(*__t); +} + +//-- TLS --// + +/* This method is invoked from within the std::__thread_specific_ptr + constructor, as such, it does not need to be thread-safe when initializing + the *__t pointer. +*/ +int __libcpp_tl_create(__libcpp_tl_key* __key, void (*__at_exit)(void*)) +{ + pthread_key_t *key = (pthread_key_t*) malloc(sizeof(pthread_key_t)); + if (key == nullptr) + return -1; + + *__key = reinterpret_cast<__libcpp_tl_key>(key); + + return pthread_key_create(AS_PTHREAD_KPTR(*__key), __at_exit); +} + +void* __libcpp_tl_get(__libcpp_tl_key __key) +{ + return pthread_getspecific(*AS_PTHREAD_KPTR(__key)); +} + +void __libcpp_tl_set(__libcpp_tl_key __key, void* __p) +{ + pthread_setspecific(*AS_PTHREAD_KPTR(__key), __p); +} + +void __libcpp_tl_finalize(__libcpp_tl_key __key) +{ + // Only deallocate the memory allocated in __libcpp_tl_create. Do not + // call pthread_key_delete(__key). See comment for __thread_specific_ptr + // destructor. + if (__key != nullptr) + free(__key); +} + +#undef AS_PTHREAD_MUTPTR +#undef AS_PTHREAD_CONDPTR +#undef AS_PTHREAD_TPTR +#undef AS_PTHREAD_KPTR + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_DYNAMIC_THREADING + +#endif // SUPPORT_EXTERNAL_THREADS