diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -18,6 +18,7 @@ include/apple_availability.h include/atomic_support.h include/config_elast.h + include/internal_threading_support.h include/refstring.h include/ryu/common.h include/ryu/d2fixed.h @@ -113,6 +114,7 @@ ) elseif(ZOS) list(APPEND LIBCXX_SOURCES + include/ceeedb.h support/ibm/mbsnrtowcs.cpp support/ibm/wcsnrtombs.cpp support/ibm/xlocale_zos.cpp diff --git a/libcxx/src/include/ceeedb.h b/libcxx/src/include/ceeedb.h new file mode 100644 --- /dev/null +++ b/libcxx/src/include/ceeedb.h @@ -0,0 +1,55 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_SUPPORT_IBM_CEEEDB_H +#define _LIBCPP_SUPPORT_IBM_CEEEDB_H + +#ifndef __MVS__ +# error This header should only be included on z/OS +#endif + +#include <__config> +#include <__gfunc.h> + +_LIBCPP_BEGIN_NAMESPACE_STD + +static inline unsigned char __libcpp_ceeedb_flags() { + // Offsets can be found in the z/OS Language Environment Vendor Interfaces book + // https://www-01.ibm.com/servers/resourcelink/svc00100.nsf/pages/zOSV2R4sa380688?OpenDocument +#ifndef __64BIT__ + enum { OFFSET_CEECAAEDB = 0x2f0 }; + enum { OFFSET_CEEEDBFLAG1 = 0x08 }; +#else + enum { OFFSET_CEECAAEDB = 0x388 }; + enum { OFFSET_CEEEDBFLAG1 = 0x100 }; +#endif + + // See the zOS Language Environment Vendor Interfaces book for a description + // of the Common Anchor Area (CEECAA), the Enclave Data Block (CEECAAEDB), + // and the EDB flags (CEEEDBFLAG1). + const unsigned char* CEECAA = static_cast(__gtca()); + const unsigned char* CEECAAEDB = + *static_cast(const_cast(static_cast(CEECAA + OFFSET_CEECAAEDB))); + const unsigned char* CEEEDBFLAG1 = (CEECAAEDB + OFFSET_CEEEDBFLAG1); + return *CEEEDBFLAG1; +} + +static inline bool __libcpp_ceeedb_multithread() { + const unsigned char CEEEDBMULTITHREAD = 0x02; + return __libcpp_ceeedb_flags() & CEEEDBMULTITHREAD; +} + +static inline bool __libcpp_ceeedb_posix() { + const unsigned char CEEEDB_POSIX = 0x04; + return __libcpp_ceeedb_flags() & CEEEDB_POSIX; +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_SUPPORT_IBM_CEEEDB_H diff --git a/libcxx/src/include/internal_threading_support.h b/libcxx/src/include/internal_threading_support.h new file mode 100644 --- /dev/null +++ b/libcxx/src/include/internal_threading_support.h @@ -0,0 +1,246 @@ +//===----------------------------------------------------------------------===//// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===//// + +#ifndef _LIBCPP_INTERNAL_THREADING_SUPPORT_H +#define _LIBCPP_INTERNAL_THREADING_SUPPORT_H + +/** internal_threading_support.h - Equivalent to __threading_support, but for internal uses that are + * not related to the C++11 Threading Library. These should all be within code that is compiled into + * the libcxx library rather than potentially #included by user code. + * + * See libcxx/docs/DesignDocs/InternalThreadSynchronization.rst for details. + * + * Any platforms which cannot determine during compilation of libc++ the availability of a thread + * library such as pthreads should use wrappers definitions provided here and define + * `__libcpp_is_threading_api_enabled()` and `__libcpp_might_have_multiple_threads()` + * following implementation on z/OS. + */ + +#include + +#ifndef _LIBCPP_HAS_NO_THREADS +# include + +# if defined(__MVS__) +static inline +bool __libcpp_is_threading_api_enabled(); + +static inline +bool __libcpp_might_have_multiple_threads(); + +# else +static inline +_LIBCPP_CONSTEXPR bool __libcpp_is_threading_api_enabled(); + +static inline +_LIBCPP_CONSTEXPR bool __libcpp_might_have_multiple_threads(); +# endif // __MVS__ + +# if defined(__MVS__) +# include "ceeedb.h" +/// On z/OS some posix functions can be enabled/disabled at runtime. +/// However, the enabled/disabled status should not change over the life of the process. +bool __libcpp_is_threading_api_enabled() { + static bool __posix_on = std::__libcpp_ceeedb_posix(); + return __posix_on; +} +bool __libcpp_might_have_multiple_threads() { return std::__libcpp_ceeedb_multithread(); } +# else +_LIBCPP_CONSTEXPR bool __libcpp_is_threading_api_enabled() { return true; } + +// Without any way of checking, we are forced to assume other threads have been spawned. +_LIBCPP_CONSTEXPR bool __libcpp_might_have_multiple_threads() { return __libcpp_is_threading_api_enabled(); } +# endif // __MVS__ + +//===----------------------------------------------------------------------===// +// Definitions which would normally be provided by <__threading_support> +// are defined here as wrappers to enable POSIX(OFF) on z/OS. +//===----------------------------------------------------------------------===// + +_LIBCPP_BEGIN_NAMESPACE_STD +namespace internal_thread { + +static inline +int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t* __m) { + if (!__libcpp_is_threading_api_enabled()) + return 0; + return _VSTD::__libcpp_recursive_mutex_init(__m); +} + +static inline +int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t* __m) { + // See __libcpp_mutex_lock for an explaination of why this is safe + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_recursive_mutex_lock(__m); +} + +static inline +bool __libcpp_recursive_mutex_trylock(__libcpp_recursive_mutex_t* __m) { + // See __libcpp_mutex_lock for an explaination of why this is safe + if (!__libcpp_might_have_multiple_threads()) + return true; + return _VSTD::__libcpp_recursive_mutex_trylock(__m); +} + +static inline +int __libcpp_recursive_mutex_unlock(__libcpp_recursive_mutex_t* __m) { + // See __libcpp_mutex_lock for an explaination of why this is safe + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_recursive_mutex_unlock(__m); +} + +static inline +int __libcpp_recursive_mutex_destroy(__libcpp_recursive_mutex_t* __m) { + if (!__libcpp_is_threading_api_enabled()) + return 0; + return _VSTD::__libcpp_recursive_mutex_destroy(__m); +} + +static inline +int __libcpp_mutex_lock(__libcpp_mutex_t* __m) { + // All libcxx-internal locks are released before we run any code which could + // spawn a thread, so we can safely skip mutex acquisition when there's only + // one thread (even if the threading API is enabled). + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_mutex_lock(__m); +} + +static inline +bool __libcpp_mutex_trylock(__libcpp_mutex_t* __m) { + // See __libcpp_mutex_lock for an explaination of why this is safe + if (!__libcpp_might_have_multiple_threads()) + return true; + return _VSTD::__libcpp_mutex_trylock(__m); +} + +static inline +int __libcpp_mutex_unlock(__libcpp_mutex_t* __m) { + // See __libcpp_mutex_lock for an explaination of why this is safe + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_mutex_unlock(__m); +} + +static inline +int __libcpp_mutex_destroy(__libcpp_mutex_t* __m) { + if (!__libcpp_is_threading_api_enabled()) + return 0; + return _VSTD::__libcpp_mutex_destroy(__m); +} + +// Condition Variable +static inline +int __libcpp_condvar_signal(__libcpp_condvar_t* __cv) { + // If we're the only thread, there's no one to signal to, skip it + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_condvar_signal(__cv); +} + +static inline +int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv) { + // If we're the only thread, there's no one to broadcast to, skip it + if (!__libcpp_might_have_multiple_threads()) + return 0; + return _VSTD::__libcpp_condvar_broadcast(__cv); +} + +static inline +int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m) { + // If we're the only thread, there's no one to wake us up, so this is a deadlock + assert(__libcpp_might_have_multiple_threads()); + return _VSTD::__libcpp_condvar_wait(__cv, __m); +} + +static inline +int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m, __libcpp_timespec_t* __ts) { + if (!__libcpp_is_threading_api_enabled()) { + // With nobody to wake us up, this is equivalent to a sleep + // TODO: actually wait until __ts, and replace this with + // `if(!__libcpp_might_have_multiple_threads())` + return ETIMEDOUT; + } + return _VSTD::__libcpp_condvar_timedwait(__cv, __m, __ts); +} + +static inline +int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv) { + if (!__libcpp_is_threading_api_enabled()) + return 0; + return _VSTD::__libcpp_condvar_destroy(__cv); +} + +// Execute once +static inline +int __libcpp_execute_once(__libcpp_exec_once_flag* flag, void (*init_routine)()) { + if (!__libcpp_is_threading_api_enabled()) { +# if defined(__MVS__) + if (*flag == _LIBCPP_EXEC_ONCE_INITIALIZER) { + init_routine(); + // TODO: In order for this to work when __libcpp_is_threading_api_enabled() can change during + // program execution, we have to write the same value pthread_once would. + // For glibc this seems to be 2, but it could vary. + *flag = 2; + } +# else + // TODO: Need a specific implementation for each platform which will allow + // __libcpp_is_threading_api_enabled() to return false. + assert(false); +# endif + return 0; + } + + return _VSTD::__libcpp_execute_once(flag, init_routine); +} + +// Thread id +static inline +int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg) { + assert(__libcpp_is_threading_api_enabled()); + return _VSTD::__libcpp_thread_create(__t, __func, __arg); +} + +static inline +__libcpp_thread_id __libcpp_thread_get_current_id() { + assert(__libcpp_is_threading_api_enabled()); + return _VSTD::__libcpp_thread_get_current_id(); +} + +static inline +int __libcpp_thread_join(__libcpp_thread_t* __t) { + assert(__libcpp_is_threading_api_enabled()); + return _VSTD::__libcpp_thread_join(__t); +} + +static inline +int __libcpp_thread_detach(__libcpp_thread_t* __t) { + assert(__libcpp_is_threading_api_enabled()); + return _VSTD::__libcpp_thread_detach(__t); +} + +static inline +void __libcpp_thread_yield() { + if (!__libcpp_might_have_multiple_threads()) + return; + _VSTD::__libcpp_thread_yield(); +} + +static inline +void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) { + assert(__libcpp_is_threading_api_enabled()); + _VSTD::__libcpp_thread_sleep_for(__ns); +} + +} // end namespace internal_thread +_LIBCPP_END_NAMESPACE_STD + +#endif // !_LIBCPP_HAS_NO_THREADS +#endif // _LIBCPP_INTERNAL_THREADING_SUPPORT_H diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp --- a/libcxx/src/locale.cpp +++ b/libcxx/src/locale.cpp @@ -25,6 +25,8 @@ #include #include +#include "include/internal_threading_support.h" + #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS # include #endif @@ -725,6 +727,12 @@ long locale::id::__get() { +#ifndef _LIBCPP_HAS_NO_THREADS + if (!__libcpp_is_threading_api_enabled()) { + __id_ = ++__next_id; + return __id_-1; + } +#endif call_once(__flag_, __fake_bind(&locale::id::__init, this)); return __id_ - 1; } diff --git a/libcxxabi/src/cxa_exception_storage.cpp b/libcxxabi/src/cxa_exception_storage.cpp --- a/libcxxabi/src/cxa_exception_storage.cpp +++ b/libcxxabi/src/cxa_exception_storage.cpp @@ -12,7 +12,7 @@ #include "cxa_exception.h" -#include "threading_support.h" +#include "include/internal_threading_support.h" // from libc++ #if defined(_LIBCXXABI_HAS_NO_THREADS) @@ -92,6 +92,11 @@ // to the Itanium ABI and is taken advantage of in several places in // libc++abi. __cxa_eh_globals *__cxa_get_globals_fast() { + // If threads are disabled at runtime, revert to single-threaded implementation. + if (!__libcpp_is_threading_api_enabled()) { + static __cxa_eh_globals eh_globals; + return &eh_globals; + } // First time through, create the key. if (0 != std::__libcpp_execute_once(&flag_, construct_)) abort_message("execute once failure in __cxa_get_globals_fast()"); diff --git a/libcxxabi/src/cxa_guard_impl.h b/libcxxabi/src/cxa_guard_impl.h --- a/libcxxabi/src/cxa_guard_impl.h +++ b/libcxxabi/src/cxa_guard_impl.h @@ -56,7 +56,7 @@ #include #include -#include "threading_support.h" +#include "include/internal_threading_support.h" // from libc++ #ifndef _LIBCXXABI_HAS_NO_THREADS # if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB) # pragma comment(lib, "pthread") @@ -152,7 +152,7 @@ #if defined(__APPLE__) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) uint32_t PlatformThreadID() { static_assert(sizeof(mach_port_t) == sizeof(uint32_t), ""); - return static_cast(pthread_mach_thread_np(std::__libcpp_thread_get_current_id())); + return static_cast(pthread_mach_thread_np(std::internal_thread::__libcpp_thread_get_current_id())); } #elif defined(SYS_gettid) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) uint32_t PlatformThreadID() { @@ -279,8 +279,8 @@ LibcppMutex(LibcppMutex const&) = delete; LibcppMutex& operator=(LibcppMutex const&) = delete; - bool lock() { return std::__libcpp_mutex_lock(&mutex); } - bool unlock() { return std::__libcpp_mutex_unlock(&mutex); } + bool lock() { return std::internal_thread::__libcpp_mutex_lock(&mutex); } + bool unlock() { return std::internal_thread::__libcpp_mutex_unlock(&mutex); } private: friend struct LibcppCondVar; @@ -292,8 +292,8 @@ LibcppCondVar(LibcppCondVar const&) = delete; LibcppCondVar& operator=(LibcppCondVar const&) = delete; - bool wait(LibcppMutex& mut) { return std::__libcpp_condvar_wait(&cond, &mut.mutex); } - bool broadcast() { return std::__libcpp_condvar_broadcast(&cond); } + bool wait(LibcppMutex& mut) { return std::internal_thread::__libcpp_condvar_wait(&cond, &mut.mutex); } + bool broadcast() { return std::internal_thread::__libcpp_condvar_broadcast(&cond); } private: std::__libcpp_condvar_t cond = _LIBCPP_CONDVAR_INITIALIZER; diff --git a/libcxxabi/test/test_exception_storage.pass.cpp b/libcxxabi/test/test_exception_storage.pass.cpp --- a/libcxxabi/test/test_exception_storage.pass.cpp +++ b/libcxxabi/test/test_exception_storage.pass.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "threading_support.h" +#include "include/internal_threading_support.h" // from libc++ #include #include "../src/cxa_exception.h" @@ -17,22 +17,22 @@ typedef __cxxabiv1::__cxa_eh_globals globals_t ; void *thread_code (void *parm) { - size_t *result = (size_t *) parm; - globals_t *glob1, *glob2; + size_t* result = (size_t*)parm; + globals_t *glob1, *glob2; - glob1 = __cxxabiv1::__cxa_get_globals (); - if ( NULL == glob1 ) - std::printf("Got null result from __cxa_get_globals\n"); + glob1 = __cxxabiv1::__cxa_get_globals(); + if (NULL == glob1) + std::printf("Got null result from __cxa_get_globals\n"); - glob2 = __cxxabiv1::__cxa_get_globals_fast (); - if ( glob1 != glob2 ) - std::printf("Got different globals!\n"); + glob2 = __cxxabiv1::__cxa_get_globals_fast(); + if (glob1 != glob2) + std::printf("Got different globals!\n"); - *result = (size_t) glob1; + *result = (size_t)glob1; #ifndef _LIBCXXABI_HAS_NO_THREADS - sleep ( 1 ); + sleep(1); #endif - return parm; + return parm; } #ifndef _LIBCXXABI_HAS_NO_THREADS @@ -42,33 +42,40 @@ #endif int main() { -#ifndef _LIBCXXABI_HAS_NO_THREADS -// Make the threads, let them run, and wait for them to finish - for ( int i = 0; i < NUMTHREADS; ++i ) - std::__libcpp_thread_create ( threads + i, thread_code, (void *) (thread_globals + i)); - for ( int i = 0; i < NUMTHREADS; ++i ) - std::__libcpp_thread_join ( &threads [ i ] ); +#ifdef _LIBCXXABI_HAS_NO_THREADS + size_t thread_globals; + thread_code(&thread_globals); + // Check that __cxa_get_globals() is not NULL. + return (thread_globals == 0) ? 1 : 0; +#else // !_LIBCXXABI_HAS_NO_THREADS + // If threads are disabled at runtime, revert to single-threaded test. + if (!__libcpp_is_threading_api_enabled()) { + thread_code((void*)thread_globals); + // Check that __cxa_get_globals() is not NULL. + return (thread_globals[0] == 0) ? 1 : 0; + } - int retVal = 0; - for ( int i = 0; i < NUMTHREADS; ++i ) { - if ( 0 == thread_globals [ i ] ) { - std::printf("Thread #%d had a zero global\n", i); - retVal = 1; - } + // Make the threads, let them run, and wait for them to finish + for (int i = 0; i < NUMTHREADS; ++i) + std::__libcpp_thread_create(threads + i, thread_code, (void*)(thread_globals + i)); + for (int i = 0; i < NUMTHREADS; ++i) + std::__libcpp_thread_join(&threads[i]); + + int retVal = 0; + for (int i = 0; i < NUMTHREADS; ++i) { + if (0 == thread_globals[i]) { + std::printf("Thread #%d had a zero global\n", i); + retVal = 1; } + } - std::sort ( thread_globals, thread_globals + NUMTHREADS ); - for ( int i = 1; i < NUMTHREADS; ++i ) { - if ( thread_globals [ i - 1 ] == thread_globals [ i ] ) { - std::printf("Duplicate thread globals (%d and %d)\n", i-1, i); - retVal = 2; - } + std::sort(thread_globals, thread_globals + NUMTHREADS); + for (int i = 1; i < NUMTHREADS; ++i) { + if (thread_globals[i - 1] == thread_globals[i]) { + std::printf("Duplicate thread globals (%d and %d)\n", i - 1, i); + retVal = 2; } - return retVal; -#else // _LIBCXXABI_HAS_NO_THREADS - size_t thread_globals; - thread_code(&thread_globals); - // Check that __cxa_get_globals() is not NULL. - return (thread_globals == 0) ? 1 : 0; + } + return retVal; #endif // !_LIBCXXABI_HAS_NO_THREADS }