libc.src.pthread.pthread_attr_setstacksize
libc.src.pthread.pthread_attr_init
libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_detach
libc.src.pthread.pthread_join
libc.src.pthread.pthread_mutex_destroy
libc.src.pthread.pthread_mutex_init
libc.src.threads.mtx_lock
libc.src.threads.mtx_unlock
libc.src.threads.thrd_create
+ libc.src.threads.thrd_detach
libc.src.threads.thrd_join
# time.h entrypoints
[ArgSpec<PThreadTType>, ArgSpec<VoidPtrPtr>]
>,
FunctionSpec<
+ "pthread_detach",
+ RetValSpec<IntType>,
+ [ArgSpec<PThreadTType>]
+ >,
+ FunctionSpec<
"pthread_mutexattr_init",
RetValSpec<IntType>,
[ArgSpec<PThreadMutexAttrTPtr>]
ArgSpec<ThrdTTypePtr>,
ArgSpec<IntPtr>,
]
+ >,
+ FunctionSpec<
+ "thrd_detach",
+ RetValSpec<IntType>,
+ [ArgSpec<ThrdTType>]
>
]
>;
thread_attrib
HDRS
thread_attrib.h
+ DEPENDS
+ libc.src.__support.common
+ libc.src.__support.CPP.atomic
)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
attrib = reinterpret_cast<ThreadAttributes<ReturnType> *>(
adjusted_stack + sizeof(StartArgs<ReturnType>));
- attrib->detached = detached;
+ attrib->detach_state =
+ uint32_t(detached ? DetachState::DETACHED : DetachState::JOINABLE);
attrib->stack = stack;
attrib->stack_size = size;
attrib->owned_stack = owned_stack;
}
int join(ReturnType *retval) {
+ wait();
+
+ *retval = attrib->retval;
+ if (attrib->owned_stack)
+ free_stack(attrib->stack, attrib->stack_size);
+
+ return 0;
+ }
+
+ // Detach a joinable thread.
+ //
+ // This method does not have error return value. However, the type of detach
+ // is returned to help with testing.
+ int detach() {
+ uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
+ if (attrib->detach_state.compare_exchange_strong(
+ joinable_state, uint32_t(DetachState::DETACHED))) {
+ return int(DetachType::SIMPLE);
+ }
+
+ // If the thread was already detached, then the detach method should not
+ // be called at all. If the thread is exiting, then we wait for it to exit
+ // and free up resources.
+ wait();
+
+ if (attrib->owned_stack)
+ free_stack(attrib->stack, attrib->stack_size);
+ return int(DetachType::CLEANUP);
+ }
+
+ // Wait for the thread to finish. This method can only be called
+ // if:
+ // 1. A detached thread is guaranteed to be running.
+ // 2. A joinable thread has not been detached or joined. As long as it has
+ // not been detached or joined, wait can be called multiple times.
+ //
+ // Also, only one thread can wait and expect to get woken up when the thread
+ // finishes.
+ //
+ // NOTE: This function is to be used for testing only. There is no standard
+ // which requires exposing it via a public API.
+ void wait() {
// The kernel should set the value at the clear tid address to zero.
// If not, it is a spurious wake and we should continue to wait on
// the futex.
__llvm_libc::syscall(SYS_futex, &clear_tid->val, FUTEX_WAIT,
CLEAR_TID_VALUE, nullptr);
}
-
- *retval = attrib->retval;
- if (!attrib->detached)
- free_stack(attrib->stack, attrib->stack_size);
-
- return 0;
}
};
auto *start_args =
reinterpret_cast<StartArgs<ReturnType> *>(get_start_args_addr());
auto *thread = start_args->thread;
- thread->attrib->retval = start_args->func(start_args->arg);
+ ReturnType retval = thread->attrib->retval =
+ start_args->func(start_args->arg);
- if (thread->attrib->detached && thread->attrib->owned_stack)
- free_stack(thread->attrib->stack, thread->attrib->stack_size);
+ uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
+ if (!thread->attrib->detach_state.compare_exchange_strong(
+ joinable_state, uint32_t(DetachState::EXITING))) {
+ // Thread is detached so cleanup the resources.
+ if (thread->attrib->owned_stack)
+ free_stack(thread->attrib->stack, thread->attrib->stack_size);
+ }
- __llvm_libc::syscall(SYS_exit, thread->attrib->retval);
+ __llvm_libc::syscall(SYS_exit, retval);
}
} // namespace __llvm_libc
#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
+#include "src/__support/CPP/atomic.h"
#include "src/__support/architectures.h"
+#include <stdint.h>
+
namespace __llvm_libc {
#if (defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86_64))
#endif
// TODO: Provide stack alignment requirements for other architectures.
+enum class DetachState : uint32_t {
+ JOINABLE = 0x11,
+ EXITING = 0x22,
+ DETACHED = 0x33
+};
+
+// Detach type is useful in testing the detach operation.
+enum class DetachType : int {
+ // Indicates that the detach operation just set the detach state to DETACHED
+ // and returned.
+ SIMPLE = 1,
+
+ // Indicates that the detach operation performed thread cleanup.
+ CLEANUP = 2
+};
+
// A data type to hold common thread attributes which have to be stored as
// thread state. Note that this is different from public attribute types like
// pthread_attr_t which might contain information which need not be saved as
// for the target architecture.
template <typename ReturnType>
struct alignas(STACK_ALIGNMENT) ThreadAttributes {
- bool detached;
+ // We want the "detach_state" attribute to be an atomic value as it could be
+ // updated by one thread while the self thread is reading it. It is a tristate
+ // variable with the following state transitions:
+ // 1. The a thread is created in a detached state, then user code should never
+ // call a detach or join function. Calling either of them can lead to
+ // undefined behavior.
+ // The value of |detach_state| is expected to be DetachState::DETACHED for
+ // its lifetime.
+ // 2. If a thread is created in a joinable state, |detach_state| will start
+ // with the value DetachState::JOINABLE. Another thread can detach this
+ // thread before it exits. The state transitions will as follows:
+ // (a) If the detach method sees the state as JOINABLE, then it will
+ // compare exchange to a state of DETACHED. The thread will clean
+ // itself up after it finishes.
+ // (b) If the detach method does not see JOINABLE in (a), then it will
+ // conclude that the thread is EXITING and will wait until the thread
+ // exits. It will clean up the thread resources once the thread
+ // exits.
+ cpp::Atomic<uint32_t> detach_state;
void *stack; // Pointer to the thread stack
unsigned long long stack_size; // Size of the stack
unsigned char owned_stack; // Indicates if the thread owns this stack memory
libc.include.pthread
libc.src.__support.threads.thread
)
+
+add_entrypoint_object(
+ pthread_detach
+ SRCS
+ pthread_detach.cpp
+ HDRS
+ pthread_detach.h
+ DEPENDS
+ libc.include.pthread
+ libc.src.__support.threads.thread
+)
--- /dev/null
+//===-- Implementation of the pthread_detach 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_detach.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<void *>),
+ "Mismatch between pthread_t and internal Thread<void *>.");
+
+LLVM_LIBC_FUNCTION(int, pthread_detach, (pthread_t th)) {
+ auto *thread = reinterpret_cast<Thread<void *> *>(&th);
+ thread->detach();
+ return 0;
+}
+
+} // namespace __llvm_libc
--- /dev/null
+//===-- Implementation header for pthread_detach 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_DETACH_H
+#define LLVM_LIBC_SRC_THREADS_PTHREAD_DETACH_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+int pthread_detach(pthread_t thread);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_DETACH_H
)
add_entrypoint_object(
+ thrd_detach
+ SRCS
+ thrd_detach.cpp
+ HDRS
+ thrd_detach.h
+ DEPENDS
+ libc.include.threads
+ libc.src.__support.threads.thread
+)
+
+add_entrypoint_object(
mtx_init
SRCS
mtx_init.cpp
--- /dev/null
+//===-- Linux implementation of the thrd_detach 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_detach.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <threads.h> // For thrd_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
+ "Mismatch between thrd_t and internal Thread<int>.");
+
+LLVM_LIBC_FUNCTION(int, thrd_detach, (thrd_t th)) {
+ auto *thread = reinterpret_cast<Thread<int> *>(&th);
+ thread->detach();
+ return 0;
+}
+
+} // namespace __llvm_libc
--- /dev/null
+//===-- Implementation header for thrd_detach 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_DETACH_H
+#define LLVM_LIBC_SRC_THREADS_THRD_DETACH_H
+
+#include "include/threads.h"
+
+namespace __llvm_libc {
+
+int thrd_detach(thrd_t thread);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_THRD_DETACH_H
add_subdirectory(CPP)
add_subdirectory(File)
add_subdirectory(OSUtil)
+add_subdirectory(threads)
--- /dev/null
+if(NOT (TARGET libc.src.__support.threads.thread AND
+ TARGET libc.src.__support.threads.mutex))
+ return()
+endif()
+
+add_libc_unittest(
+ thread_detach_test
+ SUITE
+ SRCS
+ thread_detach_test.cpp
+ DEPENDS
+ libc.src.__support.threads.mutex
+ libc.src.__support.threads.thread
+ libc.src.__support.threads.thread_attrib
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer
+)
--- /dev/null
+//===-- Unittests for thrd_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/__support/threads/mutex.h"
+#include "src/__support/threads/thread.h"
+#include "src/__support/threads/thread_attrib.h"
+#include "utils/UnitTest/Test.h"
+
+__llvm_libc::Mutex mutex(false, false, false);
+
+int func(void *) {
+ mutex.lock();
+ mutex.unlock();
+ return 0;
+}
+
+TEST(LlvmLibcTestThreadTest, DetachSimpleTest) {
+ mutex.lock();
+ __llvm_libc::Thread<int> th;
+ th.run(func, nullptr, nullptr, 0);
+
+ // Since |mutex| is held by the current thread, we guarantee that
+ // th is running and hence it is safe to detach. Since the thread is
+ // still running, it should be simple detach.
+ ASSERT_EQ(th.detach(), int(__llvm_libc::DetachType::SIMPLE));
+
+ // We will release |mutex| now to let the thread finish an cleanup itself.
+ mutex.unlock();
+}
+
+TEST(LlvmLibcTestThreadTest, DetachCleanupTest) {
+ mutex.lock();
+ __llvm_libc::Thread<int> th;
+ ASSERT_EQ(0, th.run(func, nullptr, nullptr, 0));
+
+ // Since |mutex| is held by the current thread, we will release it
+ // to let |th| run.
+ mutex.unlock();
+
+ // We will wait for |th| to finish. Since it is a joinable thread,
+ // we can wait on it safely.
+ th.wait();
+
+ // Since |th| is now finished, detaching should cleanup the thread
+ // resources.
+ ASSERT_EQ(th.detach(), int(__llvm_libc::DetachType::CLEANUP));
+}