Skip to content

Commit

Permalink
[libc] Add pthread_detach and thrd_detach.
Browse files Browse the repository at this point in the history
Tests for pthread_detach and thrd_detach have not been added. Instead, a
test for the underlying implementation has been added as it makes use of
an internal wait method to synchronize with detached threads.

Reviewed By: lntue, michaelrj

Differential Revision: https://reviews.llvm.org/D127479
  • Loading branch information
Siva Chandra Reddy committed Jun 11, 2022
1 parent e06faed commit 5db4177
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 12 deletions.
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ if(LLVM_LIBC_FULL_BUILD)
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
Expand Down Expand Up @@ -305,6 +306,7 @@ if(LLVM_LIBC_FULL_BUILD)
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
Expand Down
5 changes: 5 additions & 0 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,11 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<PThreadTType>, ArgSpec<VoidPtrPtr>]
>,
FunctionSpec<
"pthread_detach",
RetValSpec<IntType>,
[ArgSpec<PThreadTType>]
>,
FunctionSpec<
"pthread_mutexattr_init",
RetValSpec<IntType>,
Expand Down
5 changes: 5 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,11 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<ThrdTTypePtr>,
ArgSpec<IntPtr>,
]
>,
FunctionSpec<
"thrd_detach",
RetValSpec<IntType>,
[ArgSpec<ThrdTType>]
>
]
>;
Expand Down
3 changes: 3 additions & 0 deletions libc/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ add_header_library(
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})
Expand Down
65 changes: 54 additions & 11 deletions libc/src/__support/threads/linux/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ template <typename ReturnType> struct Thread {

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;
Expand Down Expand Up @@ -189,6 +190,48 @@ template <typename ReturnType> struct Thread {
}

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.
Expand All @@ -198,12 +241,6 @@ template <typename ReturnType> struct Thread {
__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;
}
};

Expand All @@ -212,12 +249,18 @@ __attribute__((noinline)) void Thread<ReturnType>::start_thread() {
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
Expand Down
39 changes: 38 additions & 1 deletion libc/src/__support/threads/thread_attrib.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,34 @@
#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))
constexpr unsigned int STACK_ALIGNMENT = 16;
#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
Expand All @@ -27,7 +46,25 @@ constexpr unsigned int STACK_ALIGNMENT = 16;
// 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
Expand Down
11 changes: 11 additions & 0 deletions libc/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,14 @@ add_entrypoint_object(
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
)
27 changes: 27 additions & 0 deletions libc/src/pthread/pthread_detach.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===-- 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
20 changes: 20 additions & 0 deletions libc/src/pthread/pthread_detach.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- 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
11 changes: 11 additions & 0 deletions libc/src/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ add_entrypoint_object(
libc.src.__support.threads.thread
)

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
Expand Down
26 changes: 26 additions & 0 deletions libc/src/threads/thrd_detach.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===-- 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
20 changes: 20 additions & 0 deletions libc/src/threads/thrd_detach.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- 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
1 change: 1 addition & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,4 @@ add_custom_command(TARGET libc_str_to_float_comparison_test
add_subdirectory(CPP)
add_subdirectory(File)
add_subdirectory(OSUtil)
add_subdirectory(threads)
18 changes: 18 additions & 0 deletions libc/test/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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
)
Loading

0 comments on commit 5db4177

Please sign in to comment.