1 //===--- A platform independent indirection for a thread class --*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H 10 #define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H 11 12 #include "src/__support/CPP/atomic.h" 13 #include "src/__support/architectures.h" 14 15 #include <stddef.h> // For size_t 16 #include <stdint.h> 17 18 namespace __llvm_libc { 19 20 using ThreadRunnerPosix = void *(void *); 21 using ThreadRunnerStdc = int(void *); 22 23 union ThreadRunner { 24 ThreadRunnerPosix *posix_runner; 25 ThreadRunnerStdc *stdc_runner; 26 }; 27 28 union ThreadReturnValue { 29 void *posix_retval; 30 int stdc_retval; 31 constexpr ThreadReturnValue() : posix_retval(nullptr) {} 32 }; 33 34 #if (defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86_64)) 35 constexpr unsigned int STACK_ALIGNMENT = 16; 36 #endif 37 // TODO: Provide stack alignment requirements for other architectures. 38 39 enum class DetachState : uint32_t { 40 JOINABLE = 0x11, 41 EXITING = 0x22, 42 DETACHED = 0x33 43 }; 44 45 enum class ThreadStyle : uint8_t { POSIX = 0x1, STDC = 0x2 }; 46 47 // Detach type is useful in testing the detach operation. 48 enum class DetachType : int { 49 // Indicates that the detach operation just set the detach state to DETACHED 50 // and returned. 51 SIMPLE = 1, 52 53 // Indicates that the detach operation performed thread cleanup. 54 CLEANUP = 2 55 }; 56 57 // A data type to hold common thread attributes which have to be stored as 58 // thread state. Note that this is different from public attribute types like 59 // pthread_attr_t which might contain information which need not be saved as 60 // part of a thread's state. For example, the stack guard size. 61 // 62 // Thread attributes are typically stored on the stack. So, we align as required 63 // for the target architecture. 64 struct alignas(STACK_ALIGNMENT) ThreadAttributes { 65 // We want the "detach_state" attribute to be an atomic value as it could be 66 // updated by one thread while the self thread is reading it. It is a tristate 67 // variable with the following state transitions: 68 // 1. The a thread is created in a detached state, then user code should never 69 // call a detach or join function. Calling either of them can lead to 70 // undefined behavior. 71 // The value of |detach_state| is expected to be DetachState::DETACHED for 72 // its lifetime. 73 // 2. If a thread is created in a joinable state, |detach_state| will start 74 // with the value DetachState::JOINABLE. Another thread can detach this 75 // thread before it exits. The state transitions will as follows: 76 // (a) If the detach method sees the state as JOINABLE, then it will 77 // compare exchange to a state of DETACHED. The thread will clean 78 // itself up after it finishes. 79 // (b) If the detach method does not see JOINABLE in (a), then it will 80 // conclude that the thread is EXITING and will wait until the thread 81 // exits. It will clean up the thread resources once the thread 82 // exits. 83 cpp::Atomic<uint32_t> detach_state; 84 void *stack; // Pointer to the thread stack 85 void *tls; 86 unsigned long long stack_size; // Size of the stack 87 unsigned char owned_stack; // Indicates if the thread owns this stack memory 88 int tid; 89 ThreadStyle style; 90 ThreadReturnValue retval; 91 void *platform_data; 92 93 constexpr ThreadAttributes() 94 : detach_state(uint32_t(DetachState::DETACHED)), stack(nullptr), 95 tls(nullptr), stack_size(0), owned_stack(false), tid(-1), 96 style(ThreadStyle::POSIX), retval(), 97 platform_data(nullptr) { 98 } 99 }; 100 101 struct Thread { 102 ThreadAttributes *attrib; 103 104 constexpr Thread() : attrib(nullptr) {} 105 constexpr Thread(ThreadAttributes *attr) : attrib(attr) {} 106 107 int run(ThreadRunnerPosix *func, void *arg, void *stack, size_t size, 108 bool detached = false) { 109 ThreadRunner runner; 110 runner.posix_runner = func; 111 return run(ThreadStyle::POSIX, runner, arg, stack, size, detached); 112 } 113 114 int run(ThreadRunnerStdc *func, void *arg, void *stack, size_t size, 115 bool detached = false) { 116 ThreadRunner runner; 117 runner.stdc_runner = func; 118 return run(ThreadStyle::STDC, runner, arg, stack, size, detached); 119 } 120 121 int join(int *val) { 122 ThreadReturnValue retval; 123 int status = join(retval); 124 if (status != 0) 125 return status; 126 *val = retval.stdc_retval; 127 return 0; 128 } 129 130 int join(void **val) { 131 ThreadReturnValue retval; 132 int status = join(retval); 133 if (status != 0) 134 return status; 135 *val = retval.posix_retval; 136 return 0; 137 } 138 139 // Platform should implement the functions below. 140 141 // Return 0 on success or an error value on failure. 142 int run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack, 143 size_t stack_size, bool detached); 144 145 // Return 0 on success or an error value on failure. 146 int join(ThreadReturnValue &retval); 147 148 // Detach a joinable thread. 149 // 150 // This method does not have error return value. However, the type of detach 151 // is returned to help with testing. 152 int detach(); 153 154 // Wait for the thread to finish. This method can only be called 155 // if: 156 // 1. A detached thread is guaranteed to be running. 157 // 2. A joinable thread has not been detached or joined. As long as it has 158 // not been detached or joined, wait can be called multiple times. 159 // 160 // Also, only one thread can wait and expect to get woken up when the thread 161 // finishes. 162 // 163 // NOTE: This function is to be used for testing only. There is no standard 164 // which requires exposing it via a public API. 165 void wait(); 166 }; 167 168 } // namespace __llvm_libc 169 170 #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H 171