25 changes: 25 additions & 0 deletions libc/src/__support/threads/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;

Expand Down
48 changes: 48 additions & 0 deletions libc/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
23 changes: 23 additions & 0 deletions libc/src/pthread/pthread_getspecific.cpp
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions libc/src/pthread/pthread_getspecific.h
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions libc/src/pthread/pthread_key_create.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/pthread/pthread_key_create.h
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions libc/src/pthread/pthread_key_delete.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/pthread/pthread_key_delete.h
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions libc/src/pthread/pthread_setspecific.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/pthread/pthread_setspecific.h
Original file line number Diff line number Diff line change
@@ -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
44 changes: 44 additions & 0 deletions libc/src/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions libc/src/threads/tss_create.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/threads/tss_create.h
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions libc/src/threads/tss_delete.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/threads/tss_delete.h
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/threads/tss_get.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/threads/tss_get.h
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions libc/src/threads/tss_set.cpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions libc/src/threads/tss_set.h
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions libc/test/integration/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
62 changes: 62 additions & 0 deletions libc/test/integration/src/pthread/pthread_tss_test.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
19 changes: 19 additions & 0 deletions libc/test/integration/src/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 63 additions & 0 deletions libc/test/integration/src/threads/tss_test.cpp
Original file line number Diff line number Diff line change
@@ -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;
}