[libc-commits] [libc] 4a738ee - [libc] Add implemementations of thread specific data related API.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu Aug 11 22:28:58 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-08-12T05:28:40Z
New Revision: 4a738ee8220e02df62b7b43d3fe5b2d6be88c482

URL: https://github.com/llvm/llvm-project/commit/4a738ee8220e02df62b7b43d3fe5b2d6be88c482
DIFF: https://github.com/llvm/llvm-project/commit/4a738ee8220e02df62b7b43d3fe5b2d6be88c482.diff

LOG: [libc] Add implemementations of thread specific data related API.

Specifically, POSIX functions pthread_key_create, pthread_key_delete,
pthread_setspecific and pthread_getspecific have been added. The C
standard equivalents tss_create, tss_delete, tss_set and tss_get have
also been added.

Reviewed By: lntue, michaelrj

Differential Revision: https://reviews.llvm.org/D131647

Added: 
    libc/include/llvm-libc-types/__pthread_tss_dtor_t.h
    libc/include/llvm-libc-types/pthread_key_t.h
    libc/include/llvm-libc-types/tss_dtor_t.h
    libc/include/llvm-libc-types/tss_t.h
    libc/src/pthread/pthread_getspecific.cpp
    libc/src/pthread/pthread_getspecific.h
    libc/src/pthread/pthread_key_create.cpp
    libc/src/pthread/pthread_key_create.h
    libc/src/pthread/pthread_key_delete.cpp
    libc/src/pthread/pthread_key_delete.h
    libc/src/pthread/pthread_setspecific.cpp
    libc/src/pthread/pthread_setspecific.h
    libc/src/threads/tss_create.cpp
    libc/src/threads/tss_create.h
    libc/src/threads/tss_delete.cpp
    libc/src/threads/tss_delete.h
    libc/src/threads/tss_get.cpp
    libc/src/threads/tss_get.h
    libc/src/threads/tss_set.cpp
    libc/src/threads/tss_set.h
    libc/test/integration/src/pthread/pthread_tss_test.cpp
    libc/test/integration/src/threads/tss_test.cpp

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/CMakeLists.txt
    libc/include/llvm-libc-types/CMakeLists.txt
    libc/spec/posix.td
    libc/spec/stdc.td
    libc/src/__support/threads/CMakeLists.txt
    libc/src/__support/threads/thread.cpp
    libc/src/__support/threads/thread.h
    libc/src/pthread/CMakeLists.txt
    libc/src/threads/CMakeLists.txt
    libc/test/integration/src/pthread/CMakeLists.txt
    libc/test/integration/src/threads/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index e7fe144b622d8..7aa23cc0c1bdd 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -231,6 +231,8 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
     "mtx_t",
     "thrd_t",
     "thrd_start_t",
+    "tss_t",
+    "tss_dtor_t",
   ];
 
   let Enumerations = [
@@ -248,10 +250,12 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
 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
index fcef386186da9..585df9eba6efa 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -253,9 +253,12 @@ if(LLVM_LIBC_FULL_BUILD)
     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 @@ if(LLVM_LIBC_FULL_BUILD)
     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 @@ if(LLVM_LIBC_FULL_BUILD)
     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
index 229abe25d23bb..faa8b1e8fcb1d 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -104,6 +104,8 @@ add_gen_header(
     .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 @@ add_gen_header(
   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
index c0e389eb355e2..e766b3e1fe2d0 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -3,6 +3,7 @@ add_header(__call_once_func_t HDR __call_once_func_t.h)
 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(off_t HDR off_t.h)
 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(struct_tm HDR struct_tm.h)
 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
index 0000000000000..1b54d31a79771
--- /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
index 0000000000000..351e37614a01e
--- /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
index 0000000000000..f80661b588ba3
--- /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
index 0000000000000..868ec1ac11288
--- /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
index 1a3177f50b810..fd5c0a9853597 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -11,6 +11,9 @@ def RestrictedStructSigactionPtr : RestrictedPtrType<StructSigaction>;
 def ConstRestrictedStructSigactionPtr : ConstType<RestrictedStructSigactionPtr>;
 
 def PThreadStartT : NamedType<"__pthread_start_t">;
+def PThreadTSSDtorT : NamedType<"__pthread_tss_dtor_t">;
+def PThreadKeyT : NamedType<"pthread_key_t">;
+def PThreadKeyTPtr : PtrType<PThreadKeyT>;
 
 def InoT : NamedType<"ino_t">;
 def DIR : NamedType<"DIR">;
@@ -406,7 +409,15 @@ def POSIX : StandardSpec<"POSIX"> {
   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 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<PThreadMutexTPtr>]
       >,
+      FunctionSpec<
+          "pthread_key_create",
+          RetValSpec<IntType>,
+          [ArgSpec<PThreadKeyTPtr>, ArgSpec<PThreadTSSDtorT>]
+      >,
+      FunctionSpec<
+          "pthread_key_delete",
+          RetValSpec<IntType>,
+          [ArgSpec<PThreadKeyT>]
+      >,
+      FunctionSpec<
+          "pthread_getspecific",
+          RetValSpec<VoidPtr>,
+          [ArgSpec<PThreadKeyT>]
+      >,
+      FunctionSpec<
+          "pthread_setspecific",
+          RetValSpec<VoidPtr>,
+          [ArgSpec<PThreadKeyT>, ArgSpec<ConstVoidPtr>]
+      >,
     ]
   >;
 

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 4e7da2b67d9af..4986262364fb8 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -8,6 +8,10 @@ def StdC : StandardSpec<"stdc"> {
   NamedType LDivTType = NamedType<"ldiv_t">;
   NamedType LLDivTType = NamedType<"lldiv_t">;
 
+  NamedType TssTType = NamedType<"tss_t">;
+  PtrType TssTPtr = PtrType<TssTType>;
+  NamedType TssDtorTType = NamedType<"tss_dtor_t">;
+
   HeaderSpec Assert = HeaderSpec<
       "assert.h",
       [
@@ -708,6 +712,8 @@ def StdC : StandardSpec<"stdc"> {
           MtxTType,
           ThrdStartTType,
           ThrdTType,
+          TssTType,
+          TssDtorTType,
       ],
       [
           EnumeratedNameValue<"mtx_plain">,
@@ -830,6 +836,26 @@ def StdC : StandardSpec<"stdc"> {
               RetValSpec<VoidType>,
               [ArgSpec<IntType>]
           >,
+          FunctionSpec<
+              "tss_create",
+              RetValSpec<IntType>,
+              [ArgSpec<TssTPtr>, ArgSpec<TssDtorTType>]
+          >,
+          FunctionSpec<
+              "tss_delete",
+              RetValSpec<IntType>,
+              [ArgSpec<TssTType>]
+          >,
+          FunctionSpec<
+              "tss_get",
+              RetValSpec<VoidPtr>,
+              [ArgSpec<TssTType>]
+          >,
+          FunctionSpec<
+              "tss_set",
+              RetValSpec<IntType>,
+              [ArgSpec<TssTType>, ArgSpec<VoidPtr>]
+          >,
       ]
   >;
 

diff  --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 9d4b0060c60f1..413989c5879d0 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -25,6 +25,7 @@ add_header_library(
   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 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
       .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.cpp b/libc/src/__support/threads/thread.cpp
index 205be944cc17a..b2680ecc0bda0 100644
--- 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 @@ struct AtExitUnit {
   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<TSSKeyUnit, TSS_KEY_COUNT> units;
+
+public:
+  constexpr TSSKeyMgr() : mtx(false, false, false) {}
+
+  cpp::optional<unsigned int> 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<unsigned int>();
+  }
+
+  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<TSSValueUnit, TSS_KEY_COUNT> tss_values;
+
 } // anonymous namespace
 
 class ThreadAtExitCallbackMgr {
@@ -74,8 +149,36 @@ ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {
 
 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<unsigned int> 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/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 1b125dd65da50..134e0c9cd9447 100644
--- 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 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
         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<unsigned int> 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/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index a1d92e6cbf4e6..43cffb6168c60 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -338,3 +338,51 @@ add_entrypoint_object(
     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.cpp b/libc/src/pthread/pthread_getspecific.cpp
new file mode 100644
index 0000000000000..02545564a8ccc
--- /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 <pthread.h>
+#include <stddef.h>
+
+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_getspecific.h b/libc/src/pthread/pthread_getspecific.h
new file mode 100644
index 0000000000000..c7da0137d5450
--- /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 <pthread.h>
+#include <stddef.h>
+
+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_key_create.cpp b/libc/src/pthread/pthread_key_create.cpp
new file mode 100644
index 0000000000000..1dc8eecfa5a21
--- /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 <errno.h>
+#include <pthread.h>
+
+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_create.h b/libc/src/pthread/pthread_key_create.h
new file mode 100644
index 0000000000000..cd5916ab01fbe
--- /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 <pthread.h>
+
+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_delete.cpp b/libc/src/pthread/pthread_key_delete.cpp
new file mode 100644
index 0000000000000..d45424cf60135
--- /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 <errno.h>
+#include <pthread.h>
+
+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_key_delete.h b/libc/src/pthread/pthread_key_delete.h
new file mode 100644
index 0000000000000..3ec528dde7197
--- /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 <pthread.h>
+
+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_setspecific.cpp b/libc/src/pthread/pthread_setspecific.cpp
new file mode 100644
index 0000000000000..15504f7f53630
--- /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 <errno.h>
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, pthread_setspecific,
+                   (pthread_key_t key, const void *data)) {
+  if (set_tss_value(key, const_cast<void *>(data)))
+    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
index 0000000000000..4211a8e79e876
--- /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 <pthread.h>
+
+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/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt
index 15efef9221e07..db011caa57760 100644
--- a/libc/src/threads/CMakeLists.txt
+++ b/libc/src/threads/CMakeLists.txt
@@ -123,6 +123,50 @@ add_entrypoint_object(
     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.cpp b/libc/src/threads/tss_create.cpp
new file mode 100644
index 0000000000000..f384565fbb9d1
--- /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 <threads.h>
+
+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_create.h b/libc/src/threads/tss_create.h
new file mode 100644
index 0000000000000..967bdca10c4fe
--- /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 <threads.h>
+
+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_delete.cpp b/libc/src/threads/tss_delete.cpp
new file mode 100644
index 0000000000000..6bdd4aa91867e
--- /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 <threads.h>
+
+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_delete.h b/libc/src/threads/tss_delete.h
new file mode 100644
index 0000000000000..1f55d3ba69f59
--- /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 <threads.h>
+
+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_get.cpp b/libc/src/threads/tss_get.cpp
new file mode 100644
index 0000000000000..4596a9337ffcc
--- /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 <threads.h>
+
+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_get.h b/libc/src/threads/tss_get.h
new file mode 100644
index 0000000000000..9f1d95ab747ed
--- /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 <pthread.h>
+
+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_set.cpp b/libc/src/threads/tss_set.cpp
new file mode 100644
index 0000000000000..977168a2a98cf
--- /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 <threads.h>
+
+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/src/threads/tss_set.h b/libc/src/threads/tss_set.h
new file mode 100644
index 0000000000000..49d69efa62ade
--- /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 <threads.h>
+
+namespace __llvm_libc {
+
+int tss_set(tss_t, void *);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_TSS_SET_H

diff  --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index a1a34beef444e..31838fb04dfe5 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -91,3 +91,22 @@ add_integration_test(
     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
index 0000000000000..4c03872f9af2f
--- /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 <pthread.h>
+
+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<int *>(data);
+  *v = THREAD_DATA_FINIVAL;
+}
+
+void *func(void *obj) {
+  ASSERT_EQ(__llvm_libc::pthread_setspecific(key, &child_thread_data), 0);
+  int *d = reinterpret_cast<int *>(__llvm_libc::pthread_getspecific(key));
+  ASSERT_TRUE(d != nullptr);
+  ASSERT_EQ(&child_thread_data, d);
+  ASSERT_EQ(*d, THREAD_DATA_INITVAL);
+  *reinterpret_cast<int *>(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<int *>(__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
index f3a70b61974fa..4dc82da58e051 100644
--- a/libc/test/integration/src/threads/CMakeLists.txt
+++ b/libc/test/integration/src/threads/CMakeLists.txt
@@ -69,6 +69,25 @@ add_integration_test(
     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
index 0000000000000..065d4f5483c81
--- /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 <threads.h>
+
+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<int *>(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<int *>(__llvm_libc::tss_get(key));
+  ASSERT_TRUE(d != nullptr);
+  ASSERT_EQ(&child_thread_data, d);
+  ASSERT_EQ(*d, THREAD_DATA_INITVAL);
+  *reinterpret_cast<int *>(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<int *>(__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;
+}


        


More information about the libc-commits mailing list