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 unsigned long long stack_size; // Size of the stack 86 uintptr_t tls; // Address to the thread TLS memory 87 uintptr_t tls_size; // The size of area pointed to by |tls|. 88 unsigned char owned_stack; // Indicates if the thread owns this stack memory 89 int tid; 90 ThreadStyle style; 91 ThreadReturnValue retval; 92 void *platform_data; 93 94 constexpr ThreadAttributes() 95 : detach_state(uint32_t(DetachState::DETACHED)), stack(nullptr), 96 stack_size(0), tls(0), tls_size(0), owned_stack(false), tid(-1), 97 style(ThreadStyle::POSIX), retval(), platform_data(nullptr) {} 98 }; 99 100 struct Thread { 101 ThreadAttributes *attrib; 102 103 constexpr Thread() : attrib(nullptr) {} 104 constexpr Thread(ThreadAttributes *attr) : attrib(attr) {} 105 106 int run(ThreadRunnerPosix *func, void *arg, void *stack, size_t size, 107 bool detached = false) { 108 ThreadRunner runner; 109 runner.posix_runner = func; 110 return run(ThreadStyle::POSIX, runner, arg, stack, size, detached); 111 } 112 113 int run(ThreadRunnerStdc *func, void *arg, void *stack, size_t size, 114 bool detached = false) { 115 ThreadRunner runner; 116 runner.stdc_runner = func; 117 return run(ThreadStyle::STDC, runner, arg, stack, size, detached); 118 } 119 120 int join(int *val) { 121 ThreadReturnValue retval; 122 int status = join(retval); 123 if (status != 0) 124 return status; 125 *val = retval.stdc_retval; 126 return 0; 127 } 128 129 int join(void **val) { 130 ThreadReturnValue retval; 131 int status = join(retval); 132 if (status != 0) 133 return status; 134 *val = retval.posix_retval; 135 return 0; 136 } 137 138 // Platform should implement the functions below. 139 140 // Return 0 on success or an error value on failure. 141 int run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack, 142 size_t stack_size, bool detached); 143 144 // Return 0 on success or an error value on failure. 145 int join(ThreadReturnValue &retval); 146 147 // Detach a joinable thread. 148 // 149 // This method does not have error return value. However, the type of detach 150 // is returned to help with testing. 151 int detach(); 152 153 // Wait for the thread to finish. This method can only be called 154 // if: 155 // 1. A detached thread is guaranteed to be running. 156 // 2. A joinable thread has not been detached or joined. As long as it has 157 // not been detached or joined, wait can be called multiple times. 158 // 159 // Also, only one thread can wait and expect to get woken up when the thread 160 // finishes. 161 // 162 // NOTE: This function is to be used for testing only. There is no standard 163 // which requires exposing it via a public API. 164 void wait(); 165 166 // Return true if this thread is equal to the other thread. 167 bool operator==(const Thread &other) const; 168 }; 169 170 extern thread_local Thread self; 171 172 } // namespace __llvm_libc 173 174 #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H 175