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