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 @@ -241,7 +241,9 @@ libc.src.pthread.pthread_attr_init libc.src.pthread.pthread_create libc.src.pthread.pthread_detach + libc.src.pthread.pthread_equal libc.src.pthread.pthread_join + libc.src.pthread.pthread_self libc.src.pthread.pthread_mutex_destroy libc.src.pthread.pthread_mutex_init libc.src.pthread.pthread_mutex_lock @@ -310,7 +312,9 @@ libc.src.threads.mtx_lock libc.src.threads.mtx_unlock libc.src.threads.thrd_create + libc.src.threads.thrd_current libc.src.threads.thrd_detach + libc.src.threads.thrd_equal libc.src.threads.thrd_join # time.h entrypoints diff --git a/libc/loader/linux/x86_64/CMakeLists.txt b/libc/loader/linux/x86_64/CMakeLists.txt --- a/libc/loader/linux/x86_64/CMakeLists.txt +++ b/libc/loader/linux/x86_64/CMakeLists.txt @@ -6,6 +6,7 @@ libc.config.linux.app_h libc.include.sys_mman libc.include.sys_syscall + libc.src.__support.threads.thread libc.src.__support.OSUtil.osutil libc.src.string.memory_utils.memcpy_implementation COMPILE_OPTIONS diff --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp --- a/libc/loader/linux/x86_64/start.cpp +++ b/libc/loader/linux/x86_64/start.cpp @@ -8,6 +8,7 @@ #include "config/linux/app.h" #include "src/__support/OSUtil/syscall.h" +#include "src/__support/threads/thread.h" #include "src/string/memory_utils/memcpy_implementations.h" #include @@ -31,6 +32,8 @@ AppProperties app; +static ThreadAttributes main_thread_attrib; + // TODO: The function is x86_64 specific. Move it to config/linux/app.h // and generalize it. Also, dynamic loading is not handled currently. void init_tls(TLSDescriptor &tls_descriptor) { @@ -98,6 +101,11 @@ }; extern "C" void _start() { + auto tid = __llvm_libc::syscall(SYS_gettid); + if (tid <= 0) + __llvm_libc::syscall(SYS_exit, 1); + __llvm_libc::main_thread_attrib.tid = tid; + // This TU is compiled with -fno-omit-frame-pointer. Hence, the previous value // of the base pointer is pushed on to the stack. So, we step over it (the // "+ 1" below) to get to the args. @@ -165,6 +173,8 @@ if (tls.size != 0 && !__llvm_libc::set_thread_ptr(tls.tp)) __llvm_libc::syscall(SYS_exit, 1); + __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib; + int retval = main(app.args->argc, reinterpret_cast(app.args->argv), reinterpret_cast(env_ptr)); __llvm_libc::cleanup_tls(tls.addr, tls.size); diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -466,6 +466,16 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "pthread_self", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "pthread_equal", + RetValSpec, + [ArgSpec, ArgSpec] + >, FunctionSpec< "pthread_mutexattr_init", RetValSpec, diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -810,6 +810,16 @@ "thrd_detach", RetValSpec, [ArgSpec] + >, + FunctionSpec< + "thrd_current", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "thrd_equal", + RetValSpec, + [ArgSpec, ArgSpec] > ] >; diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp --- a/libc/src/__support/threads/linux/thread.cpp +++ b/libc/src/__support/threads/linux/thread.cpp @@ -111,6 +111,8 @@ static void start_thread() { auto *start_args = reinterpret_cast(get_start_args_addr()); auto *attrib = start_args->thread_attrib; + self.attrib = attrib; + long retval; if (attrib->style == ThreadStyle::POSIX) { attrib->retval.posix_retval = @@ -272,4 +274,8 @@ } } +bool Thread::operator==(const Thread &thread) const { + return attrib->tid == thread.attrib->tid; +} + } // namespace __llvm_libc 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 @@ -162,8 +162,13 @@ // NOTE: This function is to be used for testing only. There is no standard // which requires exposing it via a public API. void wait(); + + // Return true if this thread is equal to the other thread. + bool operator==(const Thread &other) const; }; +extern thread_local Thread self; + } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H 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 @@ -6,5 +6,10 @@ // //===----------------------------------------------------------------------===// -// This file is currently emtpy. In future, declaration of a self thread object -// etc. will be added here. +#include "thread.h" + +namespace __llvm_libc { + +thread_local Thread self; + +} // 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 @@ -279,3 +279,25 @@ libc.include.pthread libc.src.__support.threads.thread ) + +add_entrypoint_object( + pthread_equal + SRCS + pthread_equal.cpp + HDRS + pthread_equal.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.thread +) + +add_entrypoint_object( + pthread_self + SRCS + pthread_self.cpp + HDRS + pthread_self.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.thread +) diff --git a/libc/src/pthread/pthread_equal.h b/libc/src/pthread/pthread_equal.h new file mode 100644 --- /dev/null +++ b/libc/src/pthread/pthread_equal.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pthread_equal 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_EQUAL_H +#define LLVM_LIBC_SRC_THREADS_PTHREAD_EQUAL_H + +#include + +namespace __llvm_libc { + +int pthread_equal(pthread_t lhs, pthread_t rhs); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_EQUAL_H diff --git a/libc/src/pthread/pthread_join.cpp b/libc/src/pthread/pthread_equal.cpp copy from libc/src/pthread/pthread_join.cpp copy to libc/src/pthread/pthread_equal.cpp --- a/libc/src/pthread/pthread_join.cpp +++ b/libc/src/pthread/pthread_equal.cpp @@ -1,4 +1,4 @@ -//===-- Linux implementation of the pthread_join function -----------------===// +//===-- Implementation of the pthread_equal function ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "pthread_join.h" +#include "pthread_equal.h" #include "src/__support/common.h" #include "src/__support/threads/thread.h" @@ -18,10 +18,10 @@ static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), "Mismatch between pthread_t and internal Thread."); -LLVM_LIBC_FUNCTION(int, pthread_join, (pthread_t th, void **retval)) { - auto *thread = reinterpret_cast(&th); - int result = thread->join(retval); - return result; +LLVM_LIBC_FUNCTION(int, pthread_equal, (pthread_t lhs, pthread_t rhs)) { + auto *lhs_internal = reinterpret_cast(&lhs); + auto *rhs_internal = reinterpret_cast(&rhs); + return *lhs_internal == *rhs_internal; } } // namespace __llvm_libc diff --git a/libc/src/pthread/pthread_join.cpp b/libc/src/pthread/pthread_join.cpp --- a/libc/src/pthread/pthread_join.cpp +++ b/libc/src/pthread/pthread_join.cpp @@ -1,4 +1,4 @@ -//===-- Linux implementation of the pthread_join function -----------------===// +//===-- 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. diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/pthread/pthread_self.h copy from libc/src/__support/threads/thread.cpp copy to libc/src/pthread/pthread_self.h --- a/libc/src/__support/threads/thread.cpp +++ b/libc/src/pthread/pthread_self.h @@ -1,4 +1,4 @@ -//===--- Definitions of common thread items ---------------------*- C++ -*-===// +//===-- Implementation header for pthread_self 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. @@ -6,5 +6,15 @@ // //===----------------------------------------------------------------------===// -// This file is currently emtpy. In future, declaration of a self thread object -// etc. will be added here. +#ifndef LLVM_LIBC_SRC_THREADS_PTHREAD_SELF_H +#define LLVM_LIBC_SRC_THREADS_PTHREAD_SELF_H + +#include + +namespace __llvm_libc { + +pthread_t pthread_self(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_SELF_H diff --git a/libc/src/pthread/pthread_join.cpp b/libc/src/pthread/pthread_self.cpp copy from libc/src/pthread/pthread_join.cpp copy to libc/src/pthread/pthread_self.cpp --- a/libc/src/pthread/pthread_join.cpp +++ b/libc/src/pthread/pthread_self.cpp @@ -1,4 +1,4 @@ -//===-- Linux implementation of the pthread_join function -----------------===// +//===-- Implementation of the pthread_self function -----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "pthread_join.h" +#include "pthread_self.h" #include "src/__support/common.h" #include "src/__support/threads/thread.h" @@ -18,10 +18,10 @@ static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), "Mismatch between pthread_t and internal Thread."); -LLVM_LIBC_FUNCTION(int, pthread_join, (pthread_t th, void **retval)) { - auto *thread = reinterpret_cast(&th); - int result = thread->join(retval); - return result; +LLVM_LIBC_FUNCTION(pthread_t, pthread_self, ()) { + pthread_t th; + th.__attrib = self.attrib; + return th; } } // 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 @@ -46,6 +46,28 @@ libc.src.__support.threads.thread ) +add_entrypoint_object( + thrd_current + SRCS + thrd_current.cpp + HDRS + thrd_current.h + DEPENDS + libc.include.threads + libc.src.__support.threads.thread +) + +add_entrypoint_object( + thrd_equal + SRCS + thrd_equal.cpp + HDRS + thrd_equal.h + DEPENDS + libc.include.threads + libc.src.__support.threads.thread +) + add_entrypoint_object( mtx_init SRCS diff --git a/libc/src/threads/thrd_current.h b/libc/src/threads/thrd_current.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/thrd_current.h @@ -0,0 +1,20 @@ +//===-- Implementation header for thrd_current 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_THRD_CURRENT_H +#define LLVM_LIBC_SRC_THREADS_THRD_CURRENT_H + +#include "include/threads.h" + +namespace __llvm_libc { + +thrd_t thrd_current(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_THRD_CURRENT_H diff --git a/libc/src/threads/thrd_current.cpp b/libc/src/threads/thrd_current.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/thrd_current.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the thrd_current 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 "src/threads/thrd_current.h" +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include // For thrd_* type definitions. + +namespace __llvm_libc { + +static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), + "Mismatch between thrd_t and internal Thread."); + +LLVM_LIBC_FUNCTION(thrd_t, thrd_current, ()) { + thrd_t th; + th.__attrib = self.attrib; + return th; +} + +} // namespace __llvm_libc diff --git a/libc/src/threads/thrd_equal.h b/libc/src/threads/thrd_equal.h new file mode 100644 --- /dev/null +++ b/libc/src/threads/thrd_equal.h @@ -0,0 +1,20 @@ +//===-- Implementation header for thrd_equal 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_THRD_EQUAL_H +#define LLVM_LIBC_SRC_THREADS_THRD_EQUAL_H + +#include "include/threads.h" + +namespace __llvm_libc { + +int thrd_equal(thrd_t lhs, thrd_t rhs); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_THREADS_THRD_EQUAL_H diff --git a/libc/src/threads/thrd_equal.cpp b/libc/src/threads/thrd_equal.cpp new file mode 100644 --- /dev/null +++ b/libc/src/threads/thrd_equal.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the thrd_equal 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 "src/threads/thrd_equal.h" +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" + +#include // For thrd_* type definitions. + +namespace __llvm_libc { + +static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), + "Mismatch between thrd_t and internal Thread."); + +LLVM_LIBC_FUNCTION(int, thrd_equal, (thrd_t lhs, thrd_t rhs)) { + auto *lhs_internal = reinterpret_cast(&lhs); + auto *rhs_internal = reinterpret_cast(&rhs); + return *lhs_internal == *rhs_internal; +} + +} // 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 @@ -32,3 +32,24 @@ libc.src.pthread.pthread_create libc.src.pthread.pthread_join ) + +add_integration_test( + pthread_equal_test + SUITE + libc-pthread-integration-tests + SRCS + pthread_equal_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.include.pthread + libc.src.errno.errno + libc.src.pthread.pthread_mutex_destroy + libc.src.pthread.pthread_mutex_init + libc.src.pthread.pthread_mutex_lock + libc.src.pthread.pthread_mutex_unlock + libc.src.pthread.pthread_create + libc.src.pthread.pthread_equal + libc.src.pthread.pthread_join + libc.src.pthread.pthread_self +) diff --git a/libc/test/integration/src/pthread/pthread_equal_test.cpp b/libc/test/integration/src/pthread/pthread_equal_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/pthread/pthread_equal_test.cpp @@ -0,0 +1,67 @@ +//===-- Tests for pthread_equal -------------------------------------------===// +// +// 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_equal.h" +#include "src/pthread/pthread_join.h" +#include "src/pthread/pthread_mutex_destroy.h" +#include "src/pthread/pthread_mutex_init.h" +#include "src/pthread/pthread_mutex_lock.h" +#include "src/pthread/pthread_mutex_unlock.h" +#include "src/pthread/pthread_self.h" + +#include "utils/IntegrationTest/test.h" + +#include + +pthread_t child_thread; +pthread_mutex_t mutex; + +static void *child_func(void *arg) { + __llvm_libc::pthread_mutex_lock(&mutex); + int *ret = reinterpret_cast(arg); + auto self = __llvm_libc::pthread_self(); + *ret = __llvm_libc::pthread_equal(child_thread, self); + __llvm_libc::pthread_mutex_unlock(&mutex); + return nullptr; +} + +int main() { + // We init and lock the mutex so that we guarantee that the child thread is + // waiting after startup. + ASSERT_EQ(__llvm_libc::pthread_mutex_init(&mutex, nullptr), 0); + ASSERT_EQ(__llvm_libc::pthread_mutex_lock(&mutex), 0); + + auto main_thread = __llvm_libc::pthread_self(); + + // The idea here is that, we start a child thread which will immediately + // wait on |mutex|. The main thread will update the global |child_thread| var + // and unlock |mutex|. This will give the child thread a chance to compare + // the result of pthread_self with the |child_thread|. The result of the + // comparison is returned in the thread arg. + int result = 0; + pthread_t th; + ASSERT_EQ(__llvm_libc::pthread_create(&th, nullptr, child_func, &result), 0); + // This new thread should of course not be equal to the main thread. + ASSERT_EQ(__llvm_libc::pthread_equal(th, main_thread), 0); + + // Set the |child_thread| global var and unlock to allow the child to perform + // the comparison. + child_thread = th; + ASSERT_EQ(__llvm_libc::pthread_mutex_unlock(&mutex), 0); + + void *retval; + ASSERT_EQ(__llvm_libc::pthread_join(th, &retval), 0); + ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr)); + // The child thread should see that pthread_self return value is the same as + // |child_thread|. + ASSERT_NE(result, 0); + + __llvm_libc::pthread_mutex_destroy(&mutex); + 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 @@ -19,6 +19,26 @@ libc.src.threads.thrd_join ) +add_integration_test( + thrd_equal_test + SUITE + libc-threads-integration-tests + SRCS + thrd_equal_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.include.threads + libc.src.threads.mtx_destroy + libc.src.threads.mtx_init + libc.src.threads.mtx_lock + libc.src.threads.mtx_unlock + libc.src.threads.thrd_create + libc.src.threads.thrd_current + libc.src.threads.thrd_equal + libc.src.threads.thrd_join +) + add_integration_test( thrd_test SUITE diff --git a/libc/test/integration/src/threads/thrd_equal_test.cpp b/libc/test/integration/src/threads/thrd_equal_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/threads/thrd_equal_test.cpp @@ -0,0 +1,68 @@ +//===-- Tests for thrd_equal ----------------------------------------------===// +// +// 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/mtx_destroy.h" +#include "src/threads/mtx_init.h" +#include "src/threads/mtx_lock.h" +#include "src/threads/mtx_unlock.h" +#include "src/threads/thrd_create.h" +#include "src/threads/thrd_current.h" +#include "src/threads/thrd_equal.h" +#include "src/threads/thrd_join.h" + +#include "utils/IntegrationTest/test.h" + +#include + +thrd_t child_thread; +mtx_t mutex; + +static int child_func(void *arg) { + __llvm_libc::mtx_lock(&mutex); + int *ret = reinterpret_cast(arg); + auto self = __llvm_libc::thrd_current(); + *ret = __llvm_libc::thrd_equal(child_thread, self); + __llvm_libc::mtx_unlock(&mutex); + return 0; +} + +int main() { + // We init and lock the mutex so that we guarantee that the child thread is + // waiting after startup. + ASSERT_EQ(__llvm_libc::mtx_init(&mutex, mtx_plain), int(thrd_success)); + ASSERT_EQ(__llvm_libc::mtx_lock(&mutex), int(thrd_success)); + + auto main_thread = __llvm_libc::thrd_current(); + + // The idea here is that, we start a child thread which will immediately + // wait on |mutex|. The main thread will update the global |child_thread| var + // and unlock |mutex|. This will give the child thread a chance to compare + // the result of thrd_self with the |child_thread|. The result of the + // comparison is returned in the thread arg. + int result = 0; + thrd_t th; + ASSERT_EQ(__llvm_libc::thrd_create(&th, child_func, &result), + int(thrd_success)); + // This new thread should of course not be equal to the main thread. + ASSERT_EQ(__llvm_libc::thrd_equal(th, main_thread), 0); + + // Set the |child_thread| global var and unlock to allow the child to perform + // the comparison. + child_thread = th; + ASSERT_EQ(__llvm_libc::mtx_unlock(&mutex), int(thrd_success)); + + int retval; + ASSERT_EQ(__llvm_libc::thrd_join(&th, &retval), int(thrd_success)); + ASSERT_EQ(retval, 0); + // The child thread should see that thrd_current return value is the same as + // |child_thread|. + ASSERT_NE(result, 0); + + __llvm_libc::mtx_destroy(&mutex); + return 0; +}