diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -231,6 +231,8 @@ "mtx_t", "thrd_t", "thrd_start_t", + "tss_t", + "tss_dtor_t", ]; let Enumerations = [ @@ -248,10 +250,12 @@ def PThreadAPI : PublicAPI<"pthread.h"> { let Types = [ "__pthread_start_t", + "__pthread_tss_dtor_t", "pthread_attr_t", "pthread_mutex_t", "pthread_mutexattr_t", "pthread_t", + "pthread_key_t", ]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -253,9 +253,12 @@ libc.src.pthread.pthread_equal libc.src.pthread.pthread_exit libc.src.pthread.pthread_getname_np + libc.src.pthread.pthread_getspecific libc.src.pthread.pthread_join libc.src.pthread.pthread_self libc.src.pthread.pthread_setname_np + libc.src.pthread.pthread_key_create + libc.src.pthread.pthread_key_delete libc.src.pthread.pthread_mutex_destroy libc.src.pthread.pthread_mutex_init libc.src.pthread.pthread_mutex_lock @@ -268,6 +271,7 @@ libc.src.pthread.pthread_mutexattr_setpshared libc.src.pthread.pthread_mutexattr_setrobust libc.src.pthread.pthread_mutexattr_settype + libc.src.pthread.pthread_setspecific # stdlib.h entrypoints libc.src.stdlib._Exit @@ -328,6 +332,10 @@ libc.src.threads.thrd_equal libc.src.threads.thrd_exit libc.src.threads.thrd_join + libc.src.threads.tss_create + libc.src.threads.tss_delete + libc.src.threads.tss_get + libc.src.threads.tss_set # time.h entrypoints libc.src.time.asctime diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -104,6 +104,8 @@ .llvm-libc-types.once_flag .llvm-libc-types.thrd_start_t .llvm-libc-types.thrd_t + .llvm-libc-types.tss_t + .llvm-libc-types.tss_dtor_t ) add_gen_header( @@ -174,7 +176,9 @@ DEPENDS .llvm_libc_common_h .llvm-libc-types.__pthread_start_t + .llvm-libc-types.__pthread_tss_dtor_t .llvm-libc-types.pthread_attr_t + .llvm-libc-types.pthread_key_t .llvm-libc-types.pthread_mutex_t .llvm-libc-types.pthread_mutexattr_t .llvm-libc-types.pthread_t diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -3,6 +3,7 @@ add_header(__futex_word HDR __futex_word.h) add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word) add_header(__pthread_start_t HDR __pthread_start_t.h) +add_header(__pthread_tss_dtor_t HDR __pthread_tss_dtor_t.h) add_header(__qsortcompare_t HDR __qsortcompare_t.h) add_header(__sighandler_t HDR __sighandler_t.h) add_header(__thread_type HDR __thread_type.h) @@ -25,6 +26,7 @@ add_header(off64_t HDR off64_t.h) add_header(once_flag HDR once_flag.h DEPENDS .__futex_word) add_header(pthread_attr_t HDR pthread_attr_t.h DEPENDS .size_t) +add_header(pthread_key_t HDR pthread_key_t.h) add_header(pthread_mutex_t HDR pthread_mutex_t.h DEPENDS .__futex_word .__mutex_type) add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type) add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h) @@ -36,4 +38,6 @@ add_header(thrd_start_t HDR thrd_start_t.h) add_header(thrd_t HDR thrd_t.h DEPENDS .__thread_type) add_header(time_t HDR time_t.h) +add_header(tss_t HDR tss_t.h) +add_header(tss_dtor_t HDR tss_dtor_t.h) add_header(__atexithandler_t HDR __atexithandler_t.h) diff --git a/libc/include/llvm-libc-types/__pthread_tss_dtor_t.h b/libc/include/llvm-libc-types/__pthread_tss_dtor_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/__pthread_tss_dtor_t.h @@ -0,0 +1,14 @@ +//===-- Definition of the type __pthread_tss_dtor_t -----------------------===// +// +// 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 __LLVM_LIBC_TYPES_PTHREAD_TSS_DTOR_T_H__ +#define __LLVM_LIBC_TYPES_PTHREAD_TSS_DTOR_T_H__ + +typedef void (*__pthread_tss_dtor_t)(void *); + +#endif // __LLVM_LIBC_TYPES_PTHREAD_TSS_DTOR_T_H__ diff --git a/libc/include/llvm-libc-types/pthread_key_t.h b/libc/include/llvm-libc-types/pthread_key_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/pthread_key_t.h @@ -0,0 +1,14 @@ +//===-- Definition of the type pthread_key_t ------------------------------===// +// +// 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 __LLVM_LIBC_TYPES_PTHREAD_KEY_T_H__ +#define __LLVM_LIBC_TYPES_PTHREAD_KEY_T_H__ + +typedef unsigned int pthread_key_t; + +#endif // __LLVM_LIBC_TYPES_PTHREAD_KEY_T_H__ diff --git a/libc/include/llvm-libc-types/tss_dtor_t.h b/libc/include/llvm-libc-types/tss_dtor_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/tss_dtor_t.h @@ -0,0 +1,14 @@ +//===-- Definition of the type tss_dtor_t ---------------------------------===// +// +// 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 __LLVM_LIBC_TYPES_TSS_DTOR_T_H__ +#define __LLVM_LIBC_TYPES_TSS_DTOR_T_H__ + +typedef void (*tss_dtor_t)(void *); + +#endif // __LLVM_LIBC_TYPES_TSS_DTOR_T_H__ diff --git a/libc/include/llvm-libc-types/tss_t.h b/libc/include/llvm-libc-types/tss_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/tss_t.h @@ -0,0 +1,14 @@ +//===-- Definition of the type tss_t --------------------------------------===// +// +// 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 __LLVM_LIBC_TYPES_TSS_T_H__ +#define __LLVM_LIBC_TYPES_TSS_T_H__ + +typedef unsigned int tss_t; + +#endif // __LLVM_LIBC_TYPES_TSS_T_H__ diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -11,6 +11,9 @@ def ConstRestrictedStructSigactionPtr : ConstType; def PThreadStartT : NamedType<"__pthread_start_t">; +def PThreadTSSDtorT : NamedType<"__pthread_tss_dtor_t">; +def PThreadKeyT : NamedType<"pthread_key_t">; +def PThreadKeyTPtr : PtrType; def InoT : NamedType<"ino_t">; def DIR : NamedType<"DIR">; @@ -406,7 +409,15 @@ HeaderSpec PThread = HeaderSpec< "pthread.h", [], // Macros - [PThreadAttrTType, PThreadMutexAttrTType, PThreadMutexTType, PThreadStartT, PThreadTType], // Types + [ + PThreadAttrTType, + PThreadKeyT, + PThreadMutexAttrTType, + PThreadMutexTType, + PThreadStartT, + PThreadTSSDtorT, + PThreadTType, + ], // Types [], // Enumerations [ FunctionSpec< @@ -569,6 +580,26 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "pthread_key_create", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "pthread_key_delete", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "pthread_getspecific", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "pthread_setspecific", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -8,6 +8,10 @@ NamedType LDivTType = NamedType<"ldiv_t">; NamedType LLDivTType = NamedType<"lldiv_t">; + NamedType TssTType = NamedType<"tss_t">; + PtrType TssTPtr = PtrType; + NamedType TssDtorTType = NamedType<"tss_dtor_t">; + HeaderSpec Assert = HeaderSpec< "assert.h", [ @@ -708,6 +712,8 @@ MtxTType, ThrdStartTType, ThrdTType, + TssTType, + TssDtorTType, ], [ EnumeratedNameValue<"mtx_plain">, @@ -830,6 +836,26 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "tss_create", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "tss_delete", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "tss_get", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "tss_set", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt --- a/libc/src/__support/threads/CMakeLists.txt +++ b/libc/src/__support/threads/CMakeLists.txt @@ -25,6 +25,7 @@ DEPENDS libc.src.__support.common libc.src.__support.CPP.atomic + libc.src.__support.CPP.optional libc.src.__support.CPP.string_view libc.src.__support.CPP.stringstream ) @@ -38,5 +39,7 @@ .mutex .${LIBC_TARGET_OS}.thread libc.src.__support.fixedvector + libc.src.__support.CPP.array + libc.src.__support.CPP.optional ) endif() diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h --- a/libc/src/__support/threads/thread.h +++ b/libc/src/__support/threads/thread.h @@ -11,6 +11,7 @@ #include "src/__support/CPP/StringView.h" #include "src/__support/CPP/atomic.h" +#include "src/__support/CPP/optional.h" #include "src/__support/CPP/stringstream.h" #include "src/__support/architectures.h" @@ -105,6 +106,30 @@ platform_data(nullptr) {} }; +using TSSDtor = void(void *); + +// Create a new TSS key and associate the |dtor| as the corresponding +// destructor. Can be used to implement public functions like +// pthread_key_create. +cpp::optional new_tss_key(TSSDtor *dtor); + +// Delete the |key|. Can be used to implement public functions like +// pthread_key_delete. +// +// Return true on success, false on failure. +bool tss_key_delete(unsigned int key); + +// Set the value associated with |key| for the current thread. Can be used +// to implement public functions like pthread_setspecific. +// +// Return true on success, false on failure. +bool set_tss_value(unsigned int key, void *value); + +// Return the value associated with |key| for the current thread. Return +// nullptr if |key| is invalid. Can be used to implement public functions like +// pthread_getspecific. +void *get_tss_value(unsigned int key); + struct Thread { ThreadAttributes *attrib; diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp --- a/libc/src/__support/threads/thread.cpp +++ b/libc/src/__support/threads/thread.cpp @@ -9,6 +9,8 @@ #include "thread.h" #include "mutex.h" +#include "src/__support/CPP/array.h" +#include "src/__support/CPP/optional.h" #include "src/__support/fixedvector.h" namespace __llvm_libc { @@ -26,6 +28,79 @@ constexpr AtExitUnit(AtExitCallback *cb, void *o) : callback(cb), obj(o) {} }; +constexpr size_t TSS_KEY_COUNT = 1024; + +struct TSSKeyUnit { + // Indicates whether is unit is active. Presence of a non-null dtor + // is not sufficient to indicate the same information as a TSS key can + // have a null destructor. + bool active = false; + + TSSDtor *dtor = nullptr; + + constexpr TSSKeyUnit() = default; + constexpr TSSKeyUnit(TSSDtor *d) : active(true), dtor(d) {} + + void reset() { + active = false; + dtor = nullptr; + } +}; + +class TSSKeyMgr { + Mutex mtx; + cpp::array units; + +public: + constexpr TSSKeyMgr() : mtx(false, false, false) {} + + cpp::optional new_key(TSSDtor *dtor) { + MutexLock lock(&mtx); + for (size_t i = 0; i < TSS_KEY_COUNT; ++i) { + TSSKeyUnit &u = units[i]; + if (!u.active) { + u = {dtor}; + return i; + } + } + return cpp::optional(); + } + + TSSDtor *get_dtor(unsigned int key) { + if (key >= TSS_KEY_COUNT) + return nullptr; + MutexLock lock(&mtx); + return units[key].dtor; + } + + bool remove_key(unsigned int key) { + if (key >= TSS_KEY_COUNT) + return false; + MutexLock lock(&mtx); + units[key].reset(); + return true; + } + + bool is_valid_key(unsigned int key) { + MutexLock lock(&mtx); + return units[key].active; + } +}; + +TSSKeyMgr tss_key_mgr; + +struct TSSValueUnit { + bool active = false; + void *payload = nullptr; + TSSDtor *dtor = nullptr; + + constexpr TSSValueUnit() = default; + constexpr TSSValueUnit(void *p, TSSDtor *d) + : active(true), payload(p), dtor(d) {} +}; + +static thread_local cpp::array tss_values; + } // anonymous namespace class ThreadAtExitCallbackMgr { @@ -74,8 +149,36 @@ void call_atexit_callbacks(ThreadAttributes *attrib) { attrib->atexit_callback_mgr->call(); + for (size_t i = 0; i < TSS_KEY_COUNT; ++i) { + TSSValueUnit &unit = tss_values[i]; + if (unit.dtor != nullptr) + unit.dtor(unit.payload); + } } } // namespace internal +cpp::optional new_tss_key(TSSDtor *dtor) { + return tss_key_mgr.new_key(dtor); +} + +bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); } + +bool set_tss_value(unsigned int key, void *val) { + if (!tss_key_mgr.is_valid_key(key)) + return false; + tss_values[key] = {val, tss_key_mgr.get_dtor(key)}; + return true; +} + +void *get_tss_value(unsigned int key) { + if (key >= TSS_KEY_COUNT) + return nullptr; + + auto &u = tss_values[key]; + if (!u.active) + return nullptr; + return u.payload; +} + } // namespace __llvm_libc diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt --- a/libc/src/pthread/CMakeLists.txt +++ b/libc/src/pthread/CMakeLists.txt @@ -338,3 +338,51 @@ libc.src.__support.CPP.stringstream libc.src.__support.threads.thread ) + +add_entrypoint_object( + pthread_key_create + SRCS + pthread_key_create.cpp + HDRS + pthread_key_create.h + DEPENDS + libc.include.errno + libc.include.pthread + libc.src.__support.threads.thread +) + +add_entrypoint_object( + pthread_key_delete + SRCS + pthread_key_delete.cpp + HDRS + pthread_key_delete.h + DEPENDS + libc.include.errno + libc.include.pthread + libc.src.__support.threads.thread +) + +add_entrypoint_object( + pthread_getspecific + SRCS + pthread_getspecific.cpp + HDRS + pthread_getspecific.h + DEPENDS + libc.include.errno + libc.include.pthread + libc.src.__support.threads.thread +) + +add_entrypoint_object( + pthread_setspecific + SRCS + pthread_setspecific.cpp + HDRS + pthread_setspecific.h + DEPENDS + libc.include.errno + libc.include.pthread + libc.src.__support.threads.thread +) diff --git a/libc/src/pthread/pthread_getspecific.h b/libc/src/pthread/pthread_getspecific.h new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_getspecific.h @@ -0,0 +1,21 @@ +//===-- Implementation header for pthread_getspecific function --*- 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 LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETSPECIFIC_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETSPECIFIC_H + +#include +#include + +namespace __llvm_libc { + +void *pthread_getspecific(pthread_key_t); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETSPECIFIC_H diff --git a/libc/src/pthread/pthread_getspecific.cpp b/libc/src/pthread/pthread_getspecific.cpp new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_getspecific.cpp @@ -0,0 +1,23 @@ +//===-- Linux implementation of the pthread_getspecific function ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_getspecific.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void *, pthread_getspecific, (pthread_key_t key)) { + return get_tss_value(key); +} + +} // namespace __llvm_libc diff --git a/libc/src/pthread/pthread_key_create.h b/libc/src/pthread/pthread_key_create.h new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_key_create.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pthread_key_create ------------*- 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 LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_CREATE_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_CREATE_H + +#include + +namespace __llvm_libc { + +int pthread_key_create(pthread_key_t *key, __pthread_tss_dtor_t dtor); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_CREATE_H diff --git a/libc/src/pthread/pthread_key_create.cpp b/libc/src/pthread/pthread_key_create.cpp new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_key_create.cpp @@ -0,0 +1,28 @@ +//===-- Implementation of the pthread_key_create --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_key_create.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, pthread_key_create, + (pthread_key_t * key, __pthread_tss_dtor_t dtor)) { + auto k = __llvm_libc::new_tss_key(dtor); + if (!k) + return EINVAL; + *key = *k; + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/pthread/pthread_key_delete.h b/libc/src/pthread/pthread_key_delete.h new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_key_delete.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pthread_key_delete ------------*- 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 LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_DELETE_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_DELETE_H + +#include + +namespace __llvm_libc { + +int pthread_key_delete(pthread_key_t key); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_KEY_DELETE_H diff --git a/libc/src/pthread/pthread_key_delete.cpp b/libc/src/pthread/pthread_key_delete.cpp new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_key_delete.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the pthread_key_delete --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_key_delete.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, pthread_key_delete, (pthread_key_t key)) { + if (__llvm_libc::tss_key_delete(key)) + return 0; + else + return EINVAL; +} + +} // namespace __llvm_libc diff --git a/libc/src/pthread/pthread_setspecific.h b/libc/src/pthread/pthread_setspecific.h new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_setspecific.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pthread_setspecific function --*- 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 LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETSPECIFIC_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETSPECIFIC_H + +#include + +namespace __llvm_libc { + +int pthread_setspecific(pthread_key_t, const void *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETSPECIFIC_H diff --git a/libc/src/pthread/pthread_setspecific.cpp b/libc/src/pthread/pthread_setspecific.cpp new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_setspecific.cpp @@ -0,0 +1,27 @@ +//===-- Linux implementation of the pthread_setspecific function ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_setspecific.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, pthread_setspecific, + (pthread_key_t key, const void *data)) { + if (set_tss_value(key, const_cast(data))) + return 0; + else + return EINVAL; +} + +} // namespace __llvm_libc diff --git a/libc/src/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt --- a/libc/src/threads/CMakeLists.txt +++ b/libc/src/threads/CMakeLists.txt @@ -123,6 +123,50 @@ libc.src.__support.threads.mutex ) +add_entrypoint_object( + tss_create + SRCS + tss_create.cpp + HDRS + tss_create.h + DEPENDS + libc.include.threads + libc.src.__support.threads.mutex +) + +add_entrypoint_object( + tss_delete + SRCS + tss_delete.cpp + HDRS + tss_delete.h + DEPENDS + libc.include.threads + libc.src.__support.threads.mutex +) + +add_entrypoint_object( + tss_get + SRCS + tss_get.cpp + HDRS + tss_get.h + DEPENDS + libc.include.threads + libc.src.__support.threads.mutex +) + +add_entrypoint_object( + tss_set + SRCS + tss_set.cpp + HDRS + tss_set.h + DEPENDS + libc.include.threads + libc.src.__support.threads.mutex +) + add_entrypoint_object( cnd_init ALIAS diff --git a/libc/src/threads/tss_create.h b/libc/src/threads/tss_create.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_create.h @@ -0,0 +1,20 @@ +//===-- Implementation header for tss_create --------------------*- 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 LLVM_LIBC_SRC_THREADS_TSS_CREATE_H +#define LLVM_LIBC_SRC_THREADS_TSS_CREATE_H + +#include + +namespace __llvm_libc { + +int tss_create(tss_t *key, tss_dtor_t dtor); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_TSS_CREATE_H diff --git a/libc/src/threads/tss_create.cpp b/libc/src/threads/tss_create.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_create.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the tss_create ----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "tss_create.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, tss_create, (tss_t * key, tss_dtor_t dtor)) { + auto k = __llvm_libc::new_tss_key(dtor); + if (!k) + return thrd_error; + *key = *k; + return thrd_success; +} + +} // namespace __llvm_libc diff --git a/libc/src/threads/tss_delete.h b/libc/src/threads/tss_delete.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_delete.h @@ -0,0 +1,20 @@ +//===-- Implementation header for tss_delete --------------------*- 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 LLVM_LIBC_SRC_THREADS_TSS_DELETE_H +#define LLVM_LIBC_SRC_THREADS_TSS_DELETE_H + +#include + +namespace __llvm_libc { + +void tss_delete(tss_t key); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_TSS_DELETE_H diff --git a/libc/src/threads/tss_delete.cpp b/libc/src/threads/tss_delete.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_delete.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of the tss_delete ----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "tss_delete.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, tss_delete, (tss_t key)) { + __llvm_libc::tss_key_delete(key); +} + +} // namespace __llvm_libc diff --git a/libc/src/threads/tss_get.h b/libc/src/threads/tss_get.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_get.h @@ -0,0 +1,20 @@ +//===-- Implementation header for tss_get function --------------*- 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 LLVM_LIBC_SRC_THREADS_TSS_GET_H +#define LLVM_LIBC_SRC_THREADS_TSS_GET_H + +#include + +namespace __llvm_libc { + +void *tss_get(pthread_key_t); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_TSS_GET_H diff --git a/libc/src/threads/tss_get.cpp b/libc/src/threads/tss_get.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_get.cpp @@ -0,0 +1,20 @@ +//===-- Linux implementation of the tss_get function ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "tss_get.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void *, tss_get, (tss_t key)) { return get_tss_value(key); } + +} // namespace __llvm_libc diff --git a/libc/src/threads/tss_set.h b/libc/src/threads/tss_set.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_set.h @@ -0,0 +1,20 @@ +//===-- Implementation header for tss_set function --------------*- 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 LLVM_LIBC_SRC_THREADS_TSS_SET_H +#define LLVM_LIBC_SRC_THREADS_TSS_SET_H + +#include + +namespace __llvm_libc { + +int tss_set(tss_t, void *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_TSS_SET_H diff --git a/libc/src/threads/tss_set.cpp b/libc/src/threads/tss_set.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/tss_set.cpp @@ -0,0 +1,25 @@ +//===-- Linux implementation of the tss_set function ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "tss_set.h" + +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, tss_set, (tss_t key, void *data)) { + if (set_tss_value(key, data)) + return thrd_success; + else + return thrd_error; +} + +} // namespace __llvm_libc diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -91,3 +91,22 @@ libc.src.pthread.pthread_exit libc.src.pthread.pthread_join ) + +add_integration_test( + pthread_tss_test + SUITE + libc-pthread-integration-tests + SRCS + pthread_tss_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.include.pthread + libc.src.pthread.pthread_create + libc.src.pthread.pthread_exit + libc.src.pthread.pthread_join + libc.src.pthread.pthread_key_create + libc.src.pthread.pthread_key_delete + libc.src.pthread.pthread_getspecific + libc.src.pthread.pthread_setspecific +) diff --git a/libc/test/integration/src/pthread/pthread_tss_test.cpp b/libc/test/integration/src/pthread/pthread_tss_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/pthread/pthread_tss_test.cpp @@ -0,0 +1,62 @@ +//===-- Tests for TSS API like pthread_setspecific etc. -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/pthread/pthread_create.h" +#include "src/pthread/pthread_exit.h" +#include "src/pthread/pthread_getspecific.h" +#include "src/pthread/pthread_join.h" +#include "src/pthread/pthread_key_create.h" +#include "src/pthread/pthread_key_delete.h" +#include "src/pthread/pthread_setspecific.h" +#include "utils/IntegrationTest/test.h" + +#include + +static constexpr int THREAD_DATA_INITVAL = 0x1234; +static constexpr int THREAD_DATA_FINIVAL = 0x4321; +static constexpr int THREAD_RUN_VAL = 0x600D; + +int child_thread_data = THREAD_DATA_INITVAL; +int main_thread_data = THREAD_DATA_INITVAL; + +pthread_key_t key; +void dtor(void *data) { + auto *v = reinterpret_cast(data); + *v = THREAD_DATA_FINIVAL; +} + +void *func(void *obj) { + ASSERT_EQ(__llvm_libc::pthread_setspecific(key, &child_thread_data), 0); + int *d = reinterpret_cast(__llvm_libc::pthread_getspecific(key)); + ASSERT_TRUE(d != nullptr); + ASSERT_EQ(&child_thread_data, d); + ASSERT_EQ(*d, THREAD_DATA_INITVAL); + *reinterpret_cast(obj) = THREAD_RUN_VAL; + return nullptr; +} + +TEST_MAIN() { + ASSERT_EQ(__llvm_libc::pthread_key_create(&key, &dtor), 0); + ASSERT_EQ(__llvm_libc::pthread_setspecific(key, &main_thread_data), 0); + int *d = reinterpret_cast(__llvm_libc::pthread_getspecific(key)); + ASSERT_TRUE(d != nullptr); + ASSERT_EQ(&main_thread_data, d); + ASSERT_EQ(*d, THREAD_DATA_INITVAL); + + pthread_t th; + int arg = 0xBAD; + ASSERT_EQ(__llvm_libc::pthread_create(&th, nullptr, &func, &arg), 0); + void *retval = &child_thread_data; // Init to some non-nullptr val. + ASSERT_EQ(__llvm_libc::pthread_join(th, &retval), 0); + ASSERT_EQ(retval, nullptr); + ASSERT_EQ(arg, THREAD_RUN_VAL); + ASSERT_EQ(child_thread_data, THREAD_DATA_FINIVAL); + + ASSERT_EQ(__llvm_libc::pthread_key_delete(key), 0); + return 0; +} diff --git a/libc/test/integration/src/threads/CMakeLists.txt b/libc/test/integration/src/threads/CMakeLists.txt --- a/libc/test/integration/src/threads/CMakeLists.txt +++ b/libc/test/integration/src/threads/CMakeLists.txt @@ -69,6 +69,25 @@ libc.src.threads.thrd_join ) +add_integration_test( + tss_test + SUITE + libc-threads-integration-tests + SRCS + tss_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.include.threads + libc.src.threads.thrd_create + libc.src.threads.thrd_exit + libc.src.threads.thrd_join + libc.src.threads.tss_create + libc.src.threads.tss_delete + libc.src.threads.tss_get + libc.src.threads.tss_set +) + add_integration_test( call_once_test SUITE diff --git a/libc/test/integration/src/threads/tss_test.cpp b/libc/test/integration/src/threads/tss_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/threads/tss_test.cpp @@ -0,0 +1,63 @@ +//===-- Tests for TSS API like tss_set, tss_get etc. ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/threads/thrd_create.h" +#include "src/threads/thrd_exit.h" +#include "src/threads/thrd_join.h" +#include "src/threads/tss_create.h" +#include "src/threads/tss_delete.h" +#include "src/threads/tss_get.h" +#include "src/threads/tss_set.h" +#include "utils/IntegrationTest/test.h" + +#include + +static constexpr int THREAD_DATA_INITVAL = 0x1234; +static constexpr int THREAD_DATA_FINIVAL = 0x4321; +static constexpr int THREAD_RUN_VAL = 0x600D; + +int child_thread_data = THREAD_DATA_INITVAL; +int main_thread_data = THREAD_DATA_INITVAL; + +tss_t key; +void dtor(void *data) { + auto *v = reinterpret_cast(data); + *v = THREAD_DATA_FINIVAL; +} + +int func(void *obj) { + ASSERT_EQ(__llvm_libc::tss_set(key, &child_thread_data), thrd_success); + int *d = reinterpret_cast(__llvm_libc::tss_get(key)); + ASSERT_TRUE(d != nullptr); + ASSERT_EQ(&child_thread_data, d); + ASSERT_EQ(*d, THREAD_DATA_INITVAL); + *reinterpret_cast(obj) = THREAD_RUN_VAL; + return 0; +} + +TEST_MAIN() { + ASSERT_EQ(__llvm_libc::tss_create(&key, &dtor), thrd_success); + ASSERT_EQ(__llvm_libc::tss_set(key, &main_thread_data), thrd_success); + int *d = reinterpret_cast(__llvm_libc::tss_get(key)); + ASSERT_TRUE(d != nullptr); + ASSERT_EQ(&main_thread_data, d); + ASSERT_EQ(*d, THREAD_DATA_INITVAL); + + thrd_t th; + int arg = 0xBAD; + ASSERT_EQ(__llvm_libc::thrd_create(&th, &func, &arg), thrd_success); + int retval = THREAD_DATA_INITVAL; // Init to some non-zero val. + ASSERT_EQ(__llvm_libc::thrd_join(th, &retval), thrd_success); + ASSERT_EQ(retval, 0); + ASSERT_EQ(arg, THREAD_RUN_VAL); + ASSERT_EQ(child_thread_data, THREAD_DATA_FINIVAL); + + __llvm_libc::tss_delete(key); + + return 0; +}