[libc] Add pthread_create and pthread_join functions.
authorSiva Chandra Reddy <sivachandra@google.com>
Wed, 25 May 2022 21:07:23 +0000 (21:07 +0000)
committerSiva Chandra Reddy <sivachandra@google.com>
Thu, 2 Jun 2022 01:47:24 +0000 (01:47 +0000)
They do not yet support all the feature/attributes in pthread_attr_t.
Future changes will add such support.

Reviewed By: lntue

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

17 files changed:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/include/llvm-libc-types/__pthread_start_t.h [new file with mode: 0644]
libc/include/llvm-libc-types/__thread_type.h [new file with mode: 0644]
libc/include/llvm-libc-types/pthread_t.h [new file with mode: 0644]
libc/include/llvm-libc-types/thrd_t.h
libc/spec/posix.td
libc/src/pthread/CMakeLists.txt
libc/src/pthread/pthread_create.cpp [new file with mode: 0644]
libc/src/pthread/pthread_create.h [new file with mode: 0644]
libc/src/pthread/pthread_join.cpp [new file with mode: 0644]
libc/src/pthread/pthread_join.h [new file with mode: 0644]
libc/test/src/pthread/CMakeLists.txt
libc/test/src/pthread/pthread_mutex_test.cpp
libc/test/src/pthread/pthread_test.cpp [new file with mode: 0644]

index f8ca321..3ab2f3a 100644 (file)
@@ -244,9 +244,11 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
 
 def PThreadAPI : PublicAPI<"pthread.h"> {
   let Types = [
+      "__pthread_start_t",
       "pthread_attr_t",
       "pthread_mutex_t",
-      "pthread_mutexattr_t"
+      "pthread_mutexattr_t",
+      "pthread_t",
   ];
 }
 
index 28607d6..ed2e248 100644 (file)
@@ -236,6 +236,8 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.pthread.pthread_attr_setstack
     libc.src.pthread.pthread_attr_setstacksize
     libc.src.pthread.pthread_attr_init
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_join
     libc.src.pthread.pthread_mutex_destroy
     libc.src.pthread.pthread_mutex_init
     libc.src.pthread.pthread_mutex_lock
index 5ffef44..afd4972 100644 (file)
@@ -162,9 +162,11 @@ add_gen_header(
   GEN_HDR pthread.h
   DEPENDS
     .llvm_libc_common_h
+    .llvm-libc-types.__pthread_start_t
     .llvm-libc-types.pthread_attr_t
     .llvm-libc-types.pthread_mutex_t
     .llvm-libc-types.pthread_mutexattr_t
+    .llvm-libc-types.pthread_t
 )
 
 # TODO: Not all platforms will have a include/sys directory. Add the sys
index 681f7aa..9ca82ad 100644 (file)
@@ -1,9 +1,11 @@
 add_header(__bsearchcompare_t HDR __bsearchcompare_t.h)
 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)
+add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word)
+add_header(__pthread_start_t HDR __pthread_start_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)
 add_header(cnd_t HDR cnd_t.h)
 add_header(cookie_io_functions_t HDR cookie_io_functions_t.h DEPENDS .off64_t)
 add_header(double_t HDR double_t.h)
@@ -22,12 +24,13 @@ 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_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)
 add_header(size_t HDR size_t.h)
 add_header(ssize_t HDR ssize_t.h)
 add_header(struct_sigaction HDR struct_sigaction.h)
 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)
+add_header(thrd_t HDR thrd_t.h DEPENDS .__thread_type)
 add_header(time_t HDR time_t.h)
 add_header(__atexithandler_t HDR __atexithandler_t.h)
diff --git a/libc/include/llvm-libc-types/__pthread_start_t.h b/libc/include/llvm-libc-types/__pthread_start_t.h
new file mode 100644 (file)
index 0000000..1e05f9b
--- /dev/null
@@ -0,0 +1,14 @@
+//===-- Definition of __pthread_start_t type ------------------------------===//
+//
+// 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_START_T_H__
+#define __LLVM_LIBC_TYPES_PTHREAD_START_T_H__
+
+typedef void *(*__pthread_start_t)(void *);
+
+#endif // __LLVM_LIBC_TYPES_PTHREAD_START_T_H__
diff --git a/libc/include/llvm-libc-types/__thread_type.h b/libc/include/llvm-libc-types/__thread_type.h
new file mode 100644 (file)
index 0000000..1d64f90
--- /dev/null
@@ -0,0 +1,17 @@
+//===-- Definition of thrd_t type -----------------------------------------===//
+//
+// 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_THREAD_TYPE_H__
+#define __LLVM_LIBC_TYPES_THREAD_TYPE_H__
+
+typedef struct {
+  void *__attrib;
+  void *__platform_attrib;
+} __thread_type;
+
+#endif // __LLVM_LIBC_TYPES_THREAD_TYPE_H__
diff --git a/libc/include/llvm-libc-types/pthread_t.h b/libc/include/llvm-libc-types/pthread_t.h
new file mode 100644 (file)
index 0000000..8130491
--- /dev/null
@@ -0,0 +1,16 @@
+//===-- Definition of pthread_t type --------------------------------------===//
+//
+// 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_T_H__
+#define __LLVM_LIBC_TYPES_PTHREAD_T_H__
+
+#include <llvm-libc-types/__thread_type.h>
+
+typedef __thread_type pthread_t;
+
+#endif // __LLVM_LIBC_TYPES_PTHREAD_T_H__
index 169f937..0743106 100644 (file)
@@ -9,11 +9,8 @@
 #ifndef __LLVM_LIBC_TYPES_THRD_T_H__
 #define __LLVM_LIBC_TYPES_THRD_T_H__
 
-#include <llvm-libc-types/__futex_word.h>
+#include <llvm-libc-types/__thread_type.h>
 
-typedef struct {
-  void *__attribs;
-  void *__platform_attribs;
-} thrd_t;
+typedef __thread_type thrd_t;
 
 #endif // __LLVM_LIBC_TYPES_THRD_T_H__
index 2436d8e..e53942d 100644 (file)
@@ -10,6 +10,8 @@ def ConstStructSigactionPtr : ConstType<StructSigactionPtr>;
 def RestrictedStructSigactionPtr : RestrictedPtrType<StructSigaction>;
 def ConstRestrictedStructSigactionPtr : ConstType<RestrictedStructSigactionPtr>;
 
+def PThreadStartT : NamedType<"__pthread_start_t">;
+
 def POSIX : StandardSpec<"POSIX"> {
   PtrType CharPtr = PtrType<CharType>;
   RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@@ -39,6 +41,10 @@ def POSIX : StandardSpec<"POSIX"> {
   ConstType ConstPThreadMutexTPtr = ConstType<PThreadMutexTPtr>;
   ConstType ConstRestrictedPThreadMutexTPtr = ConstType<RestrictedPThreadMutexTPtr>;
 
+  NamedType PThreadTType = NamedType<"pthread_t">;
+  PtrType PThreadTPtr = PtrType<PThreadTType>;
+  RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;
+
   HeaderSpec Errno = HeaderSpec<
       "errno.h",
       [
@@ -392,7 +398,7 @@ def POSIX : StandardSpec<"POSIX"> {
   HeaderSpec PThread = HeaderSpec<
     "pthread.h",
     [], // Macros
-    [PThreadAttrTType, PThreadMutexAttrTType, PThreadMutexTType], // Types
+    [PThreadAttrTType, PThreadMutexAttrTType, PThreadMutexTType, PThreadStartT, PThreadTType], // Types
     [], // Enumerations
     [
       FunctionSpec<
@@ -446,6 +452,16 @@ def POSIX : StandardSpec<"POSIX"> {
           [ArgSpec<PThreadAttrTPtr>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
       >,
       FunctionSpec<
+          "pthread_create",
+          RetValSpec<IntType>,
+          [ArgSpec<RestrictedPThreadTPtr>, ArgSpec<ConstRestrictedPThreadAttrTPtr>, ArgSpec<PThreadStartT>, ArgSpec<VoidPtr>]
+      >,
+      FunctionSpec<
+          "pthread_join",
+          RetValSpec<IntType>,
+          [ArgSpec<PThreadTType>, ArgSpec<VoidPtrPtr>]
+      >,
+      FunctionSpec<
           "pthread_mutexattr_init",
           RetValSpec<IntType>,
           [ArgSpec<PThreadMutexAttrTPtr>]
index ca5f0e0..828fee2 100644 (file)
@@ -242,3 +242,26 @@ add_entrypoint_object(
     libc.include.pthread
     libc.src.__support.threads.mutex
 )
+
+add_entrypoint_object(
+  pthread_create
+  SRCS
+    pthread_create.cpp
+  HDRS
+    pthread_create.h
+  DEPENDS
+    libc.include.errno
+    libc.include.pthread
+    libc.src.__support.threads.thread
+)
+
+add_entrypoint_object(
+  pthread_join
+  SRCS
+    pthread_join.cpp
+  HDRS
+    pthread_join.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.thread
+)
diff --git a/libc/src/pthread/pthread_create.cpp b/libc/src/pthread/pthread_create.cpp
new file mode 100644 (file)
index 0000000..bc945a9
--- /dev/null
@@ -0,0 +1,33 @@
+//===-- Linux implementation of the pthread_create 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_create.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <errno.h>
+#include <pthread.h> // For pthread_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread<int>),
+              "Mismatch between pthread_t and internal Thread<int>.");
+
+LLVM_LIBC_FUNCTION(int, pthread_create,
+                   (pthread_t *__restrict th,
+                    const pthread_attr_t *__restrict attr,
+                    __pthread_start_t func, void *arg)) {
+  auto *thread = reinterpret_cast<__llvm_libc::Thread<void *> *>(th);
+  int result = thread->run(func, arg, nullptr, 0);
+  if (result != 0 && result != EPERM)
+    return EAGAIN;
+  return result;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/pthread/pthread_create.h b/libc/src/pthread/pthread_create.h
new file mode 100644 (file)
index 0000000..500aa6c
--- /dev/null
@@ -0,0 +1,22 @@
+//===-- Implementation header for pthread_create 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_PTHREAD_CREATE_H
+#define LLVM_LIBC_SRC_THREADS_PTHREAD_CREATE_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+int pthread_create(pthread_t *__restrict thread,
+                   const pthread_attr_t *__restrict attr,
+                   __pthread_start_t func, void *arg);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_CREATE_H
diff --git a/libc/src/pthread/pthread_join.cpp b/libc/src/pthread/pthread_join.cpp
new file mode 100644 (file)
index 0000000..c3bf4ad
--- /dev/null
@@ -0,0 +1,27 @@
+//===-- Linux implementation of the pthread_join 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_join.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <pthread.h> // For pthread_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread<int>),
+              "Mismatch between pthread_t and internal Thread<int>.");
+
+LLVM_LIBC_FUNCTION(int, pthread_join, (pthread_t th, void **retval)) {
+  auto *thread = reinterpret_cast<Thread<void *> *>(&th);
+  int result = thread->join(retval);
+  return result;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/pthread/pthread_join.h b/libc/src/pthread/pthread_join.h
new file mode 100644 (file)
index 0000000..d659897
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation header for pthread_join 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_PTHREAD_JOIN_H
+#define LLVM_LIBC_SRC_THREADS_PTHREAD_JOIN_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+int pthread_join(pthread_t thread, void **retval);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_JOIN_H
index 5f3bbcb..f69c77f 100644 (file)
@@ -41,7 +41,7 @@ add_libc_unittest(
 )
 
 add_libc_unittest(
-  phtread_mutex_test
+  pthread_mutex_test
   SUITE
     libc_pthread_unittests
   SRCS
@@ -53,6 +53,18 @@ add_libc_unittest(
     libc.src.pthread.pthread_mutex_init
     libc.src.pthread.pthread_mutex_lock
     libc.src.pthread.pthread_mutex_unlock
-    libc.src.threads.thrd_create
-    libc.src.threads.thrd_join
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_join
+)
+
+add_libc_unittest(
+  pthread_test
+  SUITE
+    libc_pthread_unittests
+  SRCS
+    pthread_test.cpp
+  DEPENDS
+    libc.include.pthread
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_join
 )
index 8b06294..28c06b0 100644 (file)
 #include "src/pthread/pthread_mutex_lock.h"
 #include "src/pthread/pthread_mutex_unlock.h"
 
-// TODO: When pthread_t type is available, use it to spawn threads instead of
-// thrd_t.
-#include "src/threads/thrd_create.h"
-#include "src/threads/thrd_join.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
 
 #include "utils/UnitTest/Test.h"
 
@@ -26,7 +24,7 @@ constexpr int MAX = 10000;
 pthread_mutex_t mutex;
 static int shared_int = START;
 
-int counter(void *arg) {
+void *counter(void *arg) {
   int last_count = START;
   while (true) {
     __llvm_libc::pthread_mutex_lock(&mutex);
@@ -38,7 +36,7 @@ int counter(void *arg) {
     if (last_count >= MAX)
       break;
   }
-  return 0;
+  return nullptr;
 }
 
 TEST(LlvmLibcMutexTest, RelayCounter) {
@@ -46,8 +44,8 @@ TEST(LlvmLibcMutexTest, RelayCounter) {
 
   // The idea of this test is that two competing threads will update
   // a counter only if the other thread has updated it.
-  thrd_t thread;
-  __llvm_libc::thrd_create(&thread, counter, nullptr);
+  pthread_t thread;
+  __llvm_libc::pthread_create(&thread, nullptr, counter, nullptr);
 
   int last_count = START;
   while (true) {
@@ -65,9 +63,9 @@ TEST(LlvmLibcMutexTest, RelayCounter) {
       break;
   }
 
-  int retval = 123;
-  __llvm_libc::thrd_join(&thread, &retval);
-  ASSERT_EQ(retval, 0);
+  void *retval = reinterpret_cast<void *>(123);
+  __llvm_libc::pthread_join(thread, &retval);
+  ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
 
   __llvm_libc::pthread_mutex_destroy(&mutex);
 }
@@ -75,7 +73,7 @@ TEST(LlvmLibcMutexTest, RelayCounter) {
 pthread_mutex_t start_lock, step_lock;
 bool started, step;
 
-int stepper(void *arg) {
+void *stepper(void *arg) {
   __llvm_libc::pthread_mutex_lock(&start_lock);
   started = true;
   __llvm_libc::pthread_mutex_unlock(&start_lock);
@@ -83,7 +81,7 @@ int stepper(void *arg) {
   __llvm_libc::pthread_mutex_lock(&step_lock);
   step = true;
   __llvm_libc::pthread_mutex_unlock(&step_lock);
-  return 0;
+  return nullptr;
 }
 
 TEST(LlvmLibcMutexTest, WaitAndStep) {
@@ -97,8 +95,8 @@ TEST(LlvmLibcMutexTest, WaitAndStep) {
   started = false;
   ASSERT_EQ(__llvm_libc::pthread_mutex_lock(&step_lock), 0);
 
-  thrd_t thread;
-  __llvm_libc::thrd_create(&thread, stepper, nullptr);
+  pthread_t thread;
+  __llvm_libc::pthread_create(&thread, nullptr, stepper, nullptr);
 
   while (true) {
     // Make sure the thread actually started.
@@ -123,9 +121,9 @@ TEST(LlvmLibcMutexTest, WaitAndStep) {
       break;
   }
 
-  int retval = 123;
-  __llvm_libc::thrd_join(&thread, &retval);
-  ASSERT_EQ(retval, 0);
+  void *retval = reinterpret_cast<void *>(123);
+  __llvm_libc::pthread_join(thread, &retval);
+  ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
 
   __llvm_libc::pthread_mutex_destroy(&start_lock);
   __llvm_libc::pthread_mutex_destroy(&step_lock);
@@ -136,7 +134,7 @@ static pthread_mutex_t multiple_waiter_lock;
 static pthread_mutex_t counter_lock;
 static int wait_count = 0;
 
-int waiter_func(void *) {
+void *waiter_func(void *) {
   __llvm_libc::pthread_mutex_lock(&counter_lock);
   ++wait_count;
   __llvm_libc::pthread_mutex_unlock(&counter_lock);
@@ -150,7 +148,7 @@ int waiter_func(void *) {
   --wait_count;
   __llvm_libc::pthread_mutex_unlock(&counter_lock);
 
-  return 0;
+  return nullptr;
 }
 
 TEST(LlvmLibcMutexTest, MultipleWaiters) {
@@ -158,9 +156,9 @@ TEST(LlvmLibcMutexTest, MultipleWaiters) {
   __llvm_libc::pthread_mutex_init(&counter_lock, nullptr);
 
   __llvm_libc::pthread_mutex_lock(&multiple_waiter_lock);
-  thrd_t waiters[THREAD_COUNT];
+  pthread_t waiters[THREAD_COUNT];
   for (int i = 0; i < THREAD_COUNT; ++i) {
-    __llvm_libc::thrd_create(waiters + i, waiter_func, nullptr);
+    __llvm_libc::pthread_create(waiters + i, nullptr, waiter_func, nullptr);
   }
 
   // Spin until the counter is incremented to the desired
@@ -176,9 +174,9 @@ TEST(LlvmLibcMutexTest, MultipleWaiters) {
 
   __llvm_libc::pthread_mutex_unlock(&multiple_waiter_lock);
 
-  int retval;
+  void *retval;
   for (int i = 0; i < THREAD_COUNT; ++i) {
-    __llvm_libc::thrd_join(waiters + i, &retval);
+    __llvm_libc::pthread_join(waiters[i], &retval);
   }
 
   ASSERT_EQ(wait_count, 0);
diff --git a/libc/test/src/pthread/pthread_test.cpp b/libc/test/src/pthread/pthread_test.cpp
new file mode 100644 (file)
index 0000000..3a3d587
--- /dev/null
@@ -0,0 +1,56 @@
+//===-- Unittests for pthread_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "utils/UnitTest/Test.h"
+
+#include <pthread.h>
+
+static constexpr int thread_count = 1000;
+static int counter = 0;
+static void *thread_func(void *) {
+  ++counter;
+  return nullptr;
+}
+
+TEST(LlvmLibcThreadTest, CreateAndJoin) {
+  for (counter = 0; counter <= thread_count;) {
+    pthread_t thread;
+    int old_counter_val = counter;
+    ASSERT_EQ(
+        __llvm_libc::pthread_create(&thread, nullptr, thread_func, nullptr), 0);
+
+    // Start with a retval we dont expect.
+    void *retval = reinterpret_cast<void *>(thread_count + 1);
+    ASSERT_EQ(__llvm_libc::pthread_join(thread, &retval), 0);
+    ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
+    ASSERT_EQ(counter, old_counter_val + 1);
+  }
+}
+
+static void *return_arg(void *arg) { return arg; }
+
+TEST(LlvmLibcThreadTest, SpawnAndJoin) {
+  pthread_t thread_list[thread_count];
+  int args[thread_count];
+
+  for (int i = 0; i < thread_count; ++i) {
+    args[i] = i;
+    ASSERT_EQ(__llvm_libc::pthread_create(thread_list + i, nullptr, return_arg,
+                                          args + i),
+              0);
+  }
+
+  for (int i = 0; i < thread_count; ++i) {
+    // Start with a retval we dont expect.
+    void *retval = reinterpret_cast<void *>(thread_count + 1);
+    ASSERT_EQ(__llvm_libc::pthread_join(thread_list[i], &retval), 0);
+    ASSERT_EQ(*reinterpret_cast<int *>(retval), i);
+  }
+}