diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 59c248871f83a..3a631c9056163 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1039,6 +1039,9 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.pthread.pthread_join libc.src.pthread.pthread_key_create libc.src.pthread.pthread_key_delete + libc.src.pthread.pthread_barrier_init + libc.src.pthread.pthread_barrier_wait + libc.src.pthread.pthread_barrier_destroy libc.src.pthread.pthread_mutex_destroy libc.src.pthread.pthread_mutex_init libc.src.pthread.pthread_mutex_lock diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt index 052a773a4fcec..0aa79e98446c1 100644 --- a/libc/hdr/CMakeLists.txt +++ b/libc/hdr/CMakeLists.txt @@ -72,6 +72,15 @@ add_proxy_header_library( libc.include.fenv ) +add_proxy_header_library( + pthread_macros + HDRS + pthread_macros.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-macros.pthread_macros + libc.include.pthread +) + add_proxy_header_library( sched_macros HDRS diff --git a/libc/hdr/pthread_macros.h b/libc/hdr/pthread_macros.h new file mode 100644 index 0000000000000..f913015abd31c --- /dev/null +++ b/libc/hdr/pthread_macros.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread.h -------------------------------===// +// +// 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_HDR_PTHREAD_MACROS_H +#define LLVM_LIBC_HDR_PTHREAD_MACROS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-macros/pthread-macros.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_PTHREAD_MACROS_H diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt index e4b3cb0faa820..2f2d6e8f0f985 100644 --- a/libc/hdr/types/CMakeLists.txt +++ b/libc/hdr/types/CMakeLists.txt @@ -237,6 +237,22 @@ add_proxy_header_library( libc.include.llvm-libc-types.pid_t ) +add_proxy_header_library( + pthread_barrier_t + HDRS + pthread_barrier_t.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.pthread_barrier_t +) + +add_proxy_header_library( + pthread_barrierattr_t + HDRS + pthread_barrierattr_t.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.pthread_barrierattr_t +) + add_proxy_header_library( atexithandler_t HDRS diff --git a/libc/hdr/types/pthread_barrier_t.h b/libc/hdr/types/pthread_barrier_t.h new file mode 100644 index 0000000000000..57bcdfc6a9901 --- /dev/null +++ b/libc/hdr/types/pthread_barrier_t.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread_barrier_t.h ---------------------===// +// +// 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_HDR_TYPES_PTHREAD_BARRIER_T_H +#define LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIER_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/pthread_barrier_t.h" + +#else // Overlay mode + +#error "Cannot overlay pthread_barrier_t" + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIER_T_H diff --git a/libc/hdr/types/pthread_barrierattr_t.h b/libc/hdr/types/pthread_barrierattr_t.h new file mode 100644 index 0000000000000..d9d14c12e4de6 --- /dev/null +++ b/libc/hdr/types/pthread_barrierattr_t.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread_barrierattr_t.h -----------------===// +// +// 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_HDR_TYPES_PTHREAD_BARRIERATTR_T_H +#define LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIERATTR_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/pthread_barrierattr_t.h" + +#else // Overlay mode + +#error "Cannot overlay pthread_barrierattr_t" + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIERATTR_T_H diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 55268d19529c7..3c16468b8110e 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -390,6 +390,8 @@ add_header_macro( .llvm-libc-types.pthread_attr_t .llvm-libc-types.pthread_condattr_t .llvm-libc-types.pthread_key_t + .llvm-libc-types.pthread_barrier_t + .llvm-libc-types.pthread_barrierattr_t .llvm-libc-types.pthread_mutex_t .llvm-libc-types.pthread_mutexattr_t .llvm-libc-types.pthread_once_t diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h index fcc6ef925e3f4..ce467b7cc4d07 100644 --- a/libc/include/llvm-libc-macros/pthread-macros.h +++ b/libc/include/llvm-libc-macros/pthread-macros.h @@ -22,6 +22,8 @@ #define PTHREAD_MUTEX_STALLED 0 #define PTHREAD_MUTEX_ROBUST 1 +#define PTHREAD_BARRIER_SERIAL_THREAD -1 + #define PTHREAD_ONCE_INIT {0} #define PTHREAD_PROCESS_PRIVATE 0 diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index b24c97301668a..ba621757a31ed 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -10,6 +10,7 @@ add_header(__exec_envp_t HDR __exec_envp_t.h) add_header(__futex_word HDR __futex_word.h) add_header(pid_t HDR pid_t.h) add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word .pid_t) +add_header(__barrier_type HDR __barrier_type.h) add_header(__pthread_once_func_t HDR __pthread_once_func_t.h) add_header(__pthread_start_t HDR __pthread_start_t.h) add_header(__pthread_tss_dtor_t HDR __pthread_tss_dtor_t.h) @@ -53,6 +54,8 @@ add_header(pthread_condattr_t HDR pthread_condattr_t.h DEPENDS .clockid_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_mutexattr_t HDR pthread_mutexattr_t.h) +add_header(pthread_barrier_t HDR pthread_barrier_t.h) +add_header(pthread_barrierattr_t HDR pthread_barrierattr_t.h) add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word) add_header(pthread_rwlock_t HDR pthread_rwlock_t.h DEPENDS .__futex_word .pid_t) add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h) diff --git a/libc/include/llvm-libc-types/__barrier_type.h b/libc/include/llvm-libc-types/__barrier_type.h new file mode 100644 index 0000000000000..59712619e917d --- /dev/null +++ b/libc/include/llvm-libc-types/__barrier_type.h @@ -0,0 +1,21 @@ +//===-- Definition of __barrier_type 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__BARRIER_TYPE_H +#define LLVM_LIBC_TYPES__BARRIER_TYPE_H + +typedef struct __attribute__((aligned(8 /* alignof (Barrier) */))) { + unsigned expected; + unsigned waiting; + bool blocking; + char entering[24 /* sizeof (CndVar) */]; + char exiting[24 /* sizeof (CndVar) */]; + char mutex[24 /* sizeof (Mutex) */]; +} __barrier_type; + +#endif // LLVM_LIBC_TYPES__BARRIER_TYPE_H diff --git a/libc/include/llvm-libc-types/pthread_barrier_t.h b/libc/include/llvm-libc-types/pthread_barrier_t.h new file mode 100644 index 0000000000000..3a3d4706d43e6 --- /dev/null +++ b/libc/include/llvm-libc-types/pthread_barrier_t.h @@ -0,0 +1,15 @@ +//===-- Definition of pthread_barrier_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_BARRIER_T_H +#define LLVM_LIBC_TYPES_PTHREAD_BARRIER_T_H + +#include "include/llvm-libc-types/__barrier_type.h" +typedef __barrier_type pthread_barrier_t; + +#endif // LLVM_LIBC_TYPES_PTHREAD_BARRIER_T_H diff --git a/libc/include/llvm-libc-types/pthread_barrierattr_t.h b/libc/include/llvm-libc-types/pthread_barrierattr_t.h new file mode 100644 index 0000000000000..064be5bfb6721 --- /dev/null +++ b/libc/include/llvm-libc-types/pthread_barrierattr_t.h @@ -0,0 +1,16 @@ +//===-- Definition of pthread_barrierattr_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_BARRIERATTR_T_H +#define LLVM_LIBC_TYPES_PTHREAD_BARRIERATTR_T_H + +typedef struct { + bool pshared; +} pthread_barrierattr_t; + +#endif // LLVM_LIBC_TYPES_PTHREAD_BARRIERATTR_T_H diff --git a/libc/include/pthread.yaml b/libc/include/pthread.yaml index 5b27e68d2f2d8..8afce2098adde 100644 --- a/libc/include/pthread.yaml +++ b/libc/include/pthread.yaml @@ -6,6 +6,8 @@ types: - type_name: pthread_once_t - type_name: pthread_mutex_t - type_name: pthread_mutexattr_t + - type_name: pthread_barrier_t + - type_name: pthread_barrierattr_t - type_name: pthread_key_t - type_name: pthread_condattr_t - type_name: __pthread_tss_dtor_t @@ -277,6 +279,26 @@ functions: arguments: - type: pthread_mutexattr_t *__restrict - type: int + - name: pthread_barrier_init + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t *__restrict + - type: const pthread_barrierattr_t *__restrict + - type: int + - name: pthread_barrier_wait + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t * + - name: pthread_barrier_destroy + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t * - name: pthread_once standards: - POSIX diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt index bd49bbb5ad2fe..3cf50b6d3f8a9 100644 --- a/libc/src/__support/threads/CMakeLists.txt +++ b/libc/src/__support/threads/CMakeLists.txt @@ -90,6 +90,17 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar) ) endif() +add_object_library( + barrier + HDRS + barrier.h + SRCS + barrier.cpp + DEPENDS + .CndVar + .mutex +) + if (LLVM_LIBC_FULL_BUILD) set(identifier_dependency_on_thread libc.src.__support.threads.thread) endif() diff --git a/libc/src/__support/threads/barrier.cpp b/libc/src/__support/threads/barrier.cpp new file mode 100644 index 0000000000000..e39fb17637633 --- /dev/null +++ b/libc/src/__support/threads/barrier.cpp @@ -0,0 +1,88 @@ +//===-- Implementation of Barrier class ------------- ---------------------===// +// +// 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/__support/threads/barrier.h" +#include "barrier.h" +#include "hdr/errno_macros.h" +#include "src/__support/threads/CndVar.h" +#include "src/__support/threads/mutex.h" + +namespace LIBC_NAMESPACE_DECL { + +int Barrier::init(Barrier *b, + [[maybe_unused]] const pthread_barrierattr_t *attr, + unsigned count) { + LIBC_ASSERT(attr == nullptr); // TODO implement barrierattr + if (count == 0) + return EINVAL; + + b->expected = count; + b->waiting = 0; + b->blocking = true; + + int err; + err = CndVar::init(&b->entering); + if (err != 0) + return err; + + err = CndVar::init(&b->exiting); + if (err != 0) + return err; + + auto mutex_err = Mutex::init(&b->m, false, false, false, false); + if (mutex_err != MutexError::NONE) + return EAGAIN; + + return 0; +} + +int Barrier::wait() { + m.lock(); + + // if the barrier is emptying out threads, wait until it finishes + while (!blocking) { + entering.wait(&m); + } + waiting++; + + if (waiting < expected) { + // block threads until waiting = expected + while (blocking) { + exiting.wait(&m); + } + } else { + // this is the last thread to call wait(), so lets wake everyone up + blocking = false; + exiting.broadcast(); + } + waiting--; + + if (waiting == 0) { + // all threads have exited the barrier, let's let the ones waiting to enter + // continue + blocking = true; + entering.broadcast(); + m.unlock(); + + // POSIX dictates that the barrier should return a special value to just one + // thread, so we can arbitrarily choose this thread + return PTHREAD_BARRIER_SERIAL_THREAD; + } + m.unlock(); + + return 0; +} + +int Barrier::destroy(Barrier *b) { + CndVar::destroy(&b->entering); + CndVar::destroy(&b->exiting); + Mutex::destroy(&b->m); + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/threads/barrier.h b/libc/src/__support/threads/barrier.h new file mode 100644 index 0000000000000..aae811a70e41c --- /dev/null +++ b/libc/src/__support/threads/barrier.h @@ -0,0 +1,50 @@ +//===-- A platform independent abstraction layer for barriers --*- 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___SUPPORT_SRC_THREADS_LINUX_BARRIER_H +#define LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_BARRIER_H + +#include "hdr/pthread_macros.h" +#include "include/llvm-libc-types/pthread_barrier_t.h" +#include "include/llvm-libc-types/pthread_barrierattr_t.h" +#include "src/__support/threads/CndVar.h" +#include "src/__support/threads/mutex.h" + +namespace LIBC_NAMESPACE_DECL { + +// NOTE: if the size of this class changes, you must ensure that the size of +// pthread_barrier_t (found in include/llvm-libc/types/pthread_barrier_t.h) is +// the same size +class Barrier { +private: + unsigned expected; + unsigned waiting; + bool blocking; + CndVar entering; + CndVar exiting; + Mutex m; + +public: + static int init(Barrier *b, const pthread_barrierattr_t *attr, + unsigned count); + static int destroy(Barrier *b); + int wait(); +}; + +static_assert( + sizeof(Barrier) <= sizeof(pthread_barrier_t), + "The public pthread_barrier_t type cannot accommodate the internal " + "barrier type."); + +static_assert(alignof(Barrier) == alignof(pthread_barrier_t), + "The public pthread_barrier_t type has a different alignment " + "than the internal barrier type."); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_BARRIER_H diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt index c8c66805667fa..e5a0a34cf46a6 100644 --- a/libc/src/pthread/CMakeLists.txt +++ b/libc/src/pthread/CMakeLists.txt @@ -271,6 +271,40 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + pthread_barrier_init + SRCS + pthread_barrier_init.cpp + HDRS + pthread_barrier_init.h + DEPENDS + libc.src.errno.errno + libc.include.pthread + libc.src.__support.threads.barrier +) + +add_entrypoint_object( + pthread_barrier_destroy + SRCS + pthread_barrier_destroy.cpp + HDRS + pthread_barrier_destroy.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.barrier +) + +add_entrypoint_object( + pthread_barrier_wait + SRCS + pthread_barrier_wait.cpp + HDRS + pthread_barrier_wait.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.barrier +) + add_entrypoint_object( pthread_mutex_init SRCS diff --git a/libc/src/pthread/pthread_barrier_destroy.cpp b/libc/src/pthread/pthread_barrier_destroy.cpp new file mode 100644 index 0000000000000..edf1513ed0a3f --- /dev/null +++ b/libc/src/pthread/pthread_barrier_destroy.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of the pthread_barrier_destroy 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_barrier_destroy.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_destroy, (pthread_barrier_t * b)) { + return Barrier::destroy(reinterpret_cast(b)); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_destroy.h b/libc/src/pthread/pthread_barrier_destroy.h new file mode 100644 index 0000000000000..e27552ce7e5ae --- /dev/null +++ b/libc/src/pthread/pthread_barrier_destroy.h @@ -0,0 +1,21 @@ +//===-- Implementation header for pthread_barrier_destroy --------*- 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_BARRIER_DESTROY_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_DESTROY_H + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_destroy(pthread_barrier_t *b); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_DESTROY_H diff --git a/libc/src/pthread/pthread_barrier_init.cpp b/libc/src/pthread/pthread_barrier_init.cpp new file mode 100644 index 0000000000000..01b4cb3c3fe6b --- /dev/null +++ b/libc/src/pthread/pthread_barrier_init.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the pthread_barrier_init 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_barrier_init.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "hdr/types/pthread_barrierattr_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_init, + (pthread_barrier_t * b, + const pthread_barrierattr_t *__restrict attr, + unsigned count)) { + return Barrier::init(reinterpret_cast(b), attr, count); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_init.h b/libc/src/pthread/pthread_barrier_init.h new file mode 100644 index 0000000000000..bb17f3f5664da --- /dev/null +++ b/libc/src/pthread/pthread_barrier_init.h @@ -0,0 +1,24 @@ +//===-- Implementation header for pthread_barrier_init ----------*- 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_BARRIER_INIT_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_INIT_H + +#include "hdr/types/pthread_barrier_t.h" +#include "hdr/types/pthread_barrierattr_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_init(pthread_barrier_t *b, + const pthread_barrierattr_t *__restrict attr, + unsigned count); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_INIT_H diff --git a/libc/src/pthread/pthread_barrier_wait.cpp b/libc/src/pthread/pthread_barrier_wait.cpp new file mode 100644 index 0000000000000..141d13a0e52d5 --- /dev/null +++ b/libc/src/pthread/pthread_barrier_wait.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of the pthread_barrier_wait 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_barrier_wait.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_wait, (pthread_barrier_t * b)) { + return reinterpret_cast(b)->wait(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_wait.h b/libc/src/pthread/pthread_barrier_wait.h new file mode 100644 index 0000000000000..16ddc06f5be6d --- /dev/null +++ b/libc/src/pthread/pthread_barrier_wait.h @@ -0,0 +1,21 @@ +//===-- Implementation header for pthread_barrier_wait ----------*- 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_BARRIER_WAIT_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_WAIT_H + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_wait(pthread_barrier_t *b); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_WAIT_H diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt index 208ba3fd43507..ce3bb9da9d58e 100644 --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -17,6 +17,23 @@ add_integration_test( libc.src.pthread.pthread_join ) +add_integration_test( + pthread_barrier_test + SUITE + libc-pthread-integration-tests + SRCS + pthread_barrier_test.cpp + DEPENDS + libc.include.pthread + libc.src.errno.errno + libc.src.pthread.pthread_barrier_destroy + libc.src.pthread.pthread_barrier_wait + libc.src.pthread.pthread_barrier_init + libc.src.pthread.pthread_create + libc.src.pthread.pthread_join + libc.src.stdio.printf +) + add_integration_test( pthread_rwlock_test SUITE diff --git a/libc/test/integration/src/pthread/pthread_barrier_test.cpp b/libc/test/integration/src/pthread/pthread_barrier_test.cpp new file mode 100644 index 0000000000000..2ba4d3ac22897 --- /dev/null +++ b/libc/test/integration/src/pthread/pthread_barrier_test.cpp @@ -0,0 +1,118 @@ +//===-- Tests for pthread_barrier_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_barrier_destroy.h" +#include "src/pthread/pthread_barrier_init.h" +#include "src/pthread/pthread_barrier_wait.h" + +#include "src/__support/CPP/atomic.h" +#include "src/pthread/pthread_create.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/stdio/printf.h" +#include "src/string/memset.h" + +#include "test/IntegrationTest/test.h" + +#include + +pthread_barrier_t barrier; +LIBC_NAMESPACE::cpp::Atomic counter; + +void *increment_counter_and_wait(void *args) { + counter.fetch_add(1); + return reinterpret_cast( + LIBC_NAMESPACE::pthread_barrier_wait(&barrier)); +} + +void single_use_barrier_test(int num_threads) { + counter.set(0); + // create n - 1 ADDITIONAL threads since the current thread will also wait at + // the barrier + pthread_t threads[num_threads - 1]; + LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t)); + ASSERT_EQ( + LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, num_threads), 0); + + for (int i = 0; i < num_threads - 1; ++i) + LIBC_NAMESPACE::pthread_create(&threads[i], nullptr, + increment_counter_and_wait, nullptr); + + uintptr_t return_val_sum = + reinterpret_cast(increment_counter_and_wait(nullptr)); + ASSERT_EQ(counter.load(), num_threads); + + // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value + for (int i = 0; i < num_threads - 1; ++i) { + void *ret; + LIBC_NAMESPACE::pthread_join(threads[i], &ret); + if (reinterpret_cast(ret) == + static_cast(PTHREAD_BARRIER_SERIAL_THREAD)) { + return_val_sum += reinterpret_cast(ret); + } else { + ASSERT_EQ(ret, 0); + } + } + ASSERT_EQ(return_val_sum, + static_cast(PTHREAD_BARRIER_SERIAL_THREAD)); + + LIBC_NAMESPACE::pthread_barrier_destroy(&barrier); +} + +void reused_barrier_test() { + counter.set(0); + const int NUM_THREADS = 30; + const int REPEAT = 20; + pthread_t threads[NUM_THREADS - 1]; // subtract 1 for main thread + LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t)); + ASSERT_EQ( + LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, NUM_THREADS), 0); + + for (int i = 0; i < REPEAT; ++i) { + for (int j = 0; j < NUM_THREADS - 1; ++j) + LIBC_NAMESPACE::pthread_create(&threads[j], nullptr, + increment_counter_and_wait, nullptr); + + uintptr_t return_val_sum = + reinterpret_cast(increment_counter_and_wait(nullptr)); + ASSERT_EQ(counter.load(), NUM_THREADS * (i + 1)); + + // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value + for (int i = 0; i < NUM_THREADS - 1; ++i) { + void *ret; + LIBC_NAMESPACE::pthread_join(threads[i], &ret); + if (reinterpret_cast(ret) == + static_cast(PTHREAD_BARRIER_SERIAL_THREAD)) { + return_val_sum += reinterpret_cast(ret); + } else { + ASSERT_EQ(ret, 0); + } + } + ASSERT_EQ(return_val_sum, + static_cast(PTHREAD_BARRIER_SERIAL_THREAD)); + } + + LIBC_NAMESPACE::pthread_barrier_destroy(&barrier); +} + +void *barrier_wait(void *in) { + return reinterpret_cast( + LIBC_NAMESPACE::pthread_barrier_wait(&barrier)); +} + +TEST_MAIN() { + // don't create any additional threads; only use main thread + single_use_barrier_test(1); + + single_use_barrier_test(30); + reused_barrier_test(); + return 0; +}