1*fe801747SSiva Chandra Reddy //===--- Implementation of a Linux thread class -----------------*- C++ -*-===//
2*fe801747SSiva Chandra Reddy //
3*fe801747SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*fe801747SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
5*fe801747SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*fe801747SSiva Chandra Reddy //
7*fe801747SSiva Chandra Reddy //===----------------------------------------------------------------------===//
8*fe801747SSiva Chandra Reddy 
9*fe801747SSiva Chandra Reddy #include "src/__support/threads/thread.h"
10*fe801747SSiva Chandra Reddy #include "src/__support/CPP/atomic.h"
11*fe801747SSiva Chandra Reddy #include "src/__support/CPP/error.h"
12*fe801747SSiva Chandra Reddy #include "src/__support/OSUtil/syscall.h"           // For syscall functions.
13*fe801747SSiva Chandra Reddy #include "src/__support/threads/linux/futex_word.h" // For FutexWordType
14*fe801747SSiva Chandra Reddy 
15*fe801747SSiva Chandra Reddy #ifdef LLVM_LIBC_ARCH_AARCH64
16*fe801747SSiva Chandra Reddy #include <arm_acle.h>
17*fe801747SSiva Chandra Reddy #endif
18*fe801747SSiva Chandra Reddy 
19*fe801747SSiva Chandra Reddy #include <linux/futex.h>
20*fe801747SSiva Chandra Reddy #include <linux/sched.h> // For CLONE_* flags.
21*fe801747SSiva Chandra Reddy #include <stdint.h>
22*fe801747SSiva Chandra Reddy #include <sys/mman.h>    // For PROT_* and MAP_* definitions.
23*fe801747SSiva Chandra Reddy #include <sys/syscall.h> // For syscall numbers.
24*fe801747SSiva Chandra Reddy 
25*fe801747SSiva Chandra Reddy namespace __llvm_libc {
26*fe801747SSiva Chandra Reddy 
27*fe801747SSiva Chandra Reddy #ifdef SYS_mmap2
28*fe801747SSiva Chandra Reddy static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2;
29*fe801747SSiva Chandra Reddy #elif SYS_mmap
30*fe801747SSiva Chandra Reddy static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
31*fe801747SSiva Chandra Reddy #else
32*fe801747SSiva Chandra Reddy #error "SYS_mmap or SYS_mmap2 not available on the target platform"
33*fe801747SSiva Chandra Reddy #endif
34*fe801747SSiva Chandra Reddy 
35*fe801747SSiva Chandra Reddy static constexpr size_t DEFAULT_STACK_SIZE = (1 << 16); // 64KB
36*fe801747SSiva Chandra Reddy static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
37*fe801747SSiva Chandra Reddy static constexpr unsigned CLONE_SYSCALL_FLAGS =
38*fe801747SSiva Chandra Reddy     CLONE_VM        // Share the memory space with the parent.
39*fe801747SSiva Chandra Reddy     | CLONE_FS      // Share the file system with the parent.
40*fe801747SSiva Chandra Reddy     | CLONE_FILES   // Share the files with the parent.
41*fe801747SSiva Chandra Reddy     | CLONE_SIGHAND // Share the signal handlers with the parent.
42*fe801747SSiva Chandra Reddy     | CLONE_THREAD  // Same thread group as the parent.
43*fe801747SSiva Chandra Reddy     | CLONE_SYSVSEM // Share a single list of System V semaphore adjustment
44*fe801747SSiva Chandra Reddy                     // values
45*fe801747SSiva Chandra Reddy     | CLONE_PARENT_SETTID   // Set child thread ID in |ptid| of the parent.
46*fe801747SSiva Chandra Reddy     | CLONE_CHILD_CLEARTID; // Let the kernel clear the tid address
47*fe801747SSiva Chandra Reddy                             // wake the joining thread.
48*fe801747SSiva Chandra Reddy // TODO: Add the CLONE_SETTLS flag and setup the TLS area correctly
49*fe801747SSiva Chandra Reddy // when making the clone syscall.
50*fe801747SSiva Chandra Reddy 
51*fe801747SSiva Chandra Reddy static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
52*fe801747SSiva Chandra Reddy   long mmap_result =
53*fe801747SSiva Chandra Reddy       __llvm_libc::syscall(MMAP_SYSCALL_NUMBER,
54*fe801747SSiva Chandra Reddy                            0, // No special address
55*fe801747SSiva Chandra Reddy                            size,
56*fe801747SSiva Chandra Reddy                            PROT_READ | PROT_WRITE,      // Read and write stack
57*fe801747SSiva Chandra Reddy                            MAP_ANONYMOUS | MAP_PRIVATE, // Process private
58*fe801747SSiva Chandra Reddy                            -1, // Not backed by any file
59*fe801747SSiva Chandra Reddy                            0   // No offset
60*fe801747SSiva Chandra Reddy       );
61*fe801747SSiva Chandra Reddy   if (mmap_result < 0 && (uintptr_t(mmap_result) >= UINTPTR_MAX - size))
62*fe801747SSiva Chandra Reddy     return cpp::Error{int(-mmap_result)};
63*fe801747SSiva Chandra Reddy   return reinterpret_cast<void *>(mmap_result);
64*fe801747SSiva Chandra Reddy }
65*fe801747SSiva Chandra Reddy 
66*fe801747SSiva Chandra Reddy static inline void free_stack(void *stack, size_t size) {
67*fe801747SSiva Chandra Reddy   __llvm_libc::syscall(SYS_munmap, stack, size);
68*fe801747SSiva Chandra Reddy }
69*fe801747SSiva Chandra Reddy 
70*fe801747SSiva Chandra Reddy struct Thread;
71*fe801747SSiva Chandra Reddy 
72*fe801747SSiva Chandra Reddy // We align the start args to 16-byte boundary as we adjust the allocated
73*fe801747SSiva Chandra Reddy // stack memory with its size. We want the adjusted address to be at a
74*fe801747SSiva Chandra Reddy // 16-byte boundary to satisfy the x86_64 and aarch64 ABI requirements.
75*fe801747SSiva Chandra Reddy // If different architecture in future requires higher alignment, then we
76*fe801747SSiva Chandra Reddy // can add a platform specific alignment spec.
77*fe801747SSiva Chandra Reddy struct alignas(STACK_ALIGNMENT) StartArgs {
78*fe801747SSiva Chandra Reddy   Thread *thread;
79*fe801747SSiva Chandra Reddy   ThreadRunner runner;
80*fe801747SSiva Chandra Reddy   void *arg;
81*fe801747SSiva Chandra Reddy };
82*fe801747SSiva Chandra Reddy 
83*fe801747SSiva Chandra Reddy __attribute__((always_inline)) inline uintptr_t get_start_args_addr() {
84*fe801747SSiva Chandra Reddy // NOTE: For __builtin_frame_address to work reliably across compilers,
85*fe801747SSiva Chandra Reddy // architectures and various optimization levels, the TU including this file
86*fe801747SSiva Chandra Reddy // should be compiled with -fno-omit-frame-pointer.
87*fe801747SSiva Chandra Reddy #ifdef LLVM_LIBC_ARCH_X86_64
88*fe801747SSiva Chandra Reddy   return reinterpret_cast<uintptr_t>(__builtin_frame_address(0))
89*fe801747SSiva Chandra Reddy          // The x86_64 call instruction pushes resume address on to the stack.
90*fe801747SSiva Chandra Reddy          // Next, The x86_64 SysV ABI requires that the frame pointer be pushed
91*fe801747SSiva Chandra Reddy          // on to the stack. So, we have to step past two 64-bit values to get
92*fe801747SSiva Chandra Reddy          // to the start args.
93*fe801747SSiva Chandra Reddy          + sizeof(uintptr_t) * 2;
94*fe801747SSiva Chandra Reddy #elif defined(LLVM_LIBC_ARCH_AARCH64)
95*fe801747SSiva Chandra Reddy   // The frame pointer after cloning the new thread in the Thread::run method
96*fe801747SSiva Chandra Reddy   // is set to the stack pointer where start args are stored. So, we fetch
97*fe801747SSiva Chandra Reddy   // from there.
98*fe801747SSiva Chandra Reddy   return reinterpret_cast<uintptr_t>(__builtin_frame_address(1));
99*fe801747SSiva Chandra Reddy #endif
100*fe801747SSiva Chandra Reddy }
101*fe801747SSiva Chandra Reddy 
102*fe801747SSiva Chandra Reddy static void start_thread() __attribute__((noinline)) {
103*fe801747SSiva Chandra Reddy   auto *start_args = reinterpret_cast<StartArgs *>(get_start_args_addr());
104*fe801747SSiva Chandra Reddy   auto *thread = start_args->thread;
105*fe801747SSiva Chandra Reddy   auto *attrib = thread->attrib;
106*fe801747SSiva Chandra Reddy   long retval;
107*fe801747SSiva Chandra Reddy   if (attrib->style == ThreadStyle::POSIX) {
108*fe801747SSiva Chandra Reddy     attrib->retval.posix_retval =
109*fe801747SSiva Chandra Reddy         start_args->runner.posix_runner(start_args->arg);
110*fe801747SSiva Chandra Reddy     retval = long(attrib->retval.posix_retval);
111*fe801747SSiva Chandra Reddy   } else {
112*fe801747SSiva Chandra Reddy     attrib->retval.stdc_retval =
113*fe801747SSiva Chandra Reddy         start_args->runner.stdc_runner(start_args->arg);
114*fe801747SSiva Chandra Reddy     retval = long(attrib->retval.stdc_retval);
115*fe801747SSiva Chandra Reddy   }
116*fe801747SSiva Chandra Reddy 
117*fe801747SSiva Chandra Reddy   uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
118*fe801747SSiva Chandra Reddy   if (!thread->attrib->detach_state.compare_exchange_strong(
119*fe801747SSiva Chandra Reddy           joinable_state, uint32_t(DetachState::EXITING))) {
120*fe801747SSiva Chandra Reddy     // Thread is detached so cleanup the resources.
121*fe801747SSiva Chandra Reddy     if (thread->attrib->owned_stack)
122*fe801747SSiva Chandra Reddy       free_stack(thread->attrib->stack, thread->attrib->stack_size);
123*fe801747SSiva Chandra Reddy   }
124*fe801747SSiva Chandra Reddy 
125*fe801747SSiva Chandra Reddy   __llvm_libc::syscall(SYS_exit, retval);
126*fe801747SSiva Chandra Reddy }
127*fe801747SSiva Chandra Reddy 
128*fe801747SSiva Chandra Reddy int Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
129*fe801747SSiva Chandra Reddy                 size_t size, bool detached) {
130*fe801747SSiva Chandra Reddy   bool owned_stack = false;
131*fe801747SSiva Chandra Reddy   if (stack == nullptr) {
132*fe801747SSiva Chandra Reddy     if (size == 0)
133*fe801747SSiva Chandra Reddy       size = DEFAULT_STACK_SIZE;
134*fe801747SSiva Chandra Reddy     auto alloc = alloc_stack(size);
135*fe801747SSiva Chandra Reddy     if (!alloc)
136*fe801747SSiva Chandra Reddy       return alloc.error_code();
137*fe801747SSiva Chandra Reddy     else
138*fe801747SSiva Chandra Reddy       stack = alloc.value();
139*fe801747SSiva Chandra Reddy     owned_stack = true;
140*fe801747SSiva Chandra Reddy   }
141*fe801747SSiva Chandra Reddy 
142*fe801747SSiva Chandra Reddy   // When the new thread is spawned by the kernel, the new thread gets the
143*fe801747SSiva Chandra Reddy   // stack we pass to the clone syscall. However, this stack is empty and does
144*fe801747SSiva Chandra Reddy   // not have any local vars present in this function. Hence, one cannot
145*fe801747SSiva Chandra Reddy   // pass arguments to the thread start function, or use any local vars from
146*fe801747SSiva Chandra Reddy   // here. So, we pack them into the new stack from where the thread can sniff
147*fe801747SSiva Chandra Reddy   // them out.
148*fe801747SSiva Chandra Reddy   //
149*fe801747SSiva Chandra Reddy   // Likewise, the actual thread state information is also stored on the
150*fe801747SSiva Chandra Reddy   // stack memory.
151*fe801747SSiva Chandra Reddy   uintptr_t adjusted_stack = reinterpret_cast<uintptr_t>(stack) + size -
152*fe801747SSiva Chandra Reddy                              sizeof(StartArgs) - sizeof(ThreadAttributes) -
153*fe801747SSiva Chandra Reddy                              sizeof(cpp::Atomic<FutexWordType>);
154*fe801747SSiva Chandra Reddy   adjusted_stack &= ~(uintptr_t(STACK_ALIGNMENT) - 1);
155*fe801747SSiva Chandra Reddy 
156*fe801747SSiva Chandra Reddy   auto *start_args = reinterpret_cast<StartArgs *>(adjusted_stack);
157*fe801747SSiva Chandra Reddy   start_args->thread = this;
158*fe801747SSiva Chandra Reddy   start_args->runner = runner;
159*fe801747SSiva Chandra Reddy   start_args->arg = arg;
160*fe801747SSiva Chandra Reddy 
161*fe801747SSiva Chandra Reddy   attrib =
162*fe801747SSiva Chandra Reddy       reinterpret_cast<ThreadAttributes *>(adjusted_stack + sizeof(StartArgs));
163*fe801747SSiva Chandra Reddy   attrib->style = style;
164*fe801747SSiva Chandra Reddy   attrib->detach_state =
165*fe801747SSiva Chandra Reddy       uint32_t(detached ? DetachState::DETACHED : DetachState::JOINABLE);
166*fe801747SSiva Chandra Reddy   attrib->stack = stack;
167*fe801747SSiva Chandra Reddy   attrib->stack_size = size;
168*fe801747SSiva Chandra Reddy   attrib->owned_stack = owned_stack;
169*fe801747SSiva Chandra Reddy 
170*fe801747SSiva Chandra Reddy   auto clear_tid = reinterpret_cast<cpp::Atomic<FutexWordType> *>(
171*fe801747SSiva Chandra Reddy       adjusted_stack + sizeof(StartArgs) + sizeof(ThreadAttributes));
172*fe801747SSiva Chandra Reddy   clear_tid->val = CLEAR_TID_VALUE;
173*fe801747SSiva Chandra Reddy   platform_data = clear_tid;
174*fe801747SSiva Chandra Reddy 
175*fe801747SSiva Chandra Reddy   // The clone syscall takes arguments in an architecture specific order.
176*fe801747SSiva Chandra Reddy   // Also, we want the result of the syscall to be in a register as the child
177*fe801747SSiva Chandra Reddy   // thread gets a completely different stack after it is created. The stack
178*fe801747SSiva Chandra Reddy   // variables from this function will not be availalbe to the child thread.
179*fe801747SSiva Chandra Reddy #ifdef LLVM_LIBC_ARCH_X86_64
180*fe801747SSiva Chandra Reddy   long register clone_result asm("rax");
181*fe801747SSiva Chandra Reddy   clone_result = __llvm_libc::syscall(
182*fe801747SSiva Chandra Reddy       SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
183*fe801747SSiva Chandra Reddy       &attrib->tid,    // The address where the child tid is written
184*fe801747SSiva Chandra Reddy       &clear_tid->val, // The futex where the child thread status is signalled
185*fe801747SSiva Chandra Reddy       0                // Set TLS to null for now.
186*fe801747SSiva Chandra Reddy   );
187*fe801747SSiva Chandra Reddy #elif defined(LLVM_LIBC_ARCH_AARCH64)
188*fe801747SSiva Chandra Reddy   long register clone_result asm("x0");
189*fe801747SSiva Chandra Reddy   clone_result = __llvm_libc::syscall(
190*fe801747SSiva Chandra Reddy       SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
191*fe801747SSiva Chandra Reddy       &attrib->tid,   // The address where the child tid is written
192*fe801747SSiva Chandra Reddy       0,              // Set TLS to null for now.
193*fe801747SSiva Chandra Reddy       &clear_tid->val // The futex where the child thread status is signalled
194*fe801747SSiva Chandra Reddy   );
195*fe801747SSiva Chandra Reddy #else
196*fe801747SSiva Chandra Reddy #error "Unsupported architecture for the clone syscall."
197*fe801747SSiva Chandra Reddy #endif
198*fe801747SSiva Chandra Reddy 
199*fe801747SSiva Chandra Reddy   if (clone_result == 0) {
200*fe801747SSiva Chandra Reddy #ifdef LLVM_LIBC_ARCH_AARCH64
201*fe801747SSiva Chandra Reddy     // We set the frame pointer to be the same as the "sp" so that start args
202*fe801747SSiva Chandra Reddy     // can be sniffed out from start_thread.
203*fe801747SSiva Chandra Reddy     __arm_wsr64("x29", __arm_rsr64("sp"));
204*fe801747SSiva Chandra Reddy #endif
205*fe801747SSiva Chandra Reddy     start_thread();
206*fe801747SSiva Chandra Reddy   } else if (clone_result < 0) {
207*fe801747SSiva Chandra Reddy     if (attrib->owned_stack)
208*fe801747SSiva Chandra Reddy       free_stack(attrib->stack, attrib->stack_size);
209*fe801747SSiva Chandra Reddy     return -clone_result;
210*fe801747SSiva Chandra Reddy   }
211*fe801747SSiva Chandra Reddy 
212*fe801747SSiva Chandra Reddy   return 0;
213*fe801747SSiva Chandra Reddy }
214*fe801747SSiva Chandra Reddy 
215*fe801747SSiva Chandra Reddy int Thread::join(ThreadReturnValue &retval) {
216*fe801747SSiva Chandra Reddy   wait();
217*fe801747SSiva Chandra Reddy 
218*fe801747SSiva Chandra Reddy   if (attrib->style == ThreadStyle::POSIX)
219*fe801747SSiva Chandra Reddy     retval.posix_retval = attrib->retval.posix_retval;
220*fe801747SSiva Chandra Reddy   else
221*fe801747SSiva Chandra Reddy     retval.stdc_retval = attrib->retval.stdc_retval;
222*fe801747SSiva Chandra Reddy 
223*fe801747SSiva Chandra Reddy   if (attrib->owned_stack)
224*fe801747SSiva Chandra Reddy     free_stack(attrib->stack, attrib->stack_size);
225*fe801747SSiva Chandra Reddy 
226*fe801747SSiva Chandra Reddy   return 0;
227*fe801747SSiva Chandra Reddy }
228*fe801747SSiva Chandra Reddy 
229*fe801747SSiva Chandra Reddy int Thread::detach() {
230*fe801747SSiva Chandra Reddy   uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
231*fe801747SSiva Chandra Reddy   if (attrib->detach_state.compare_exchange_strong(
232*fe801747SSiva Chandra Reddy           joinable_state, uint32_t(DetachState::DETACHED))) {
233*fe801747SSiva Chandra Reddy     return int(DetachType::SIMPLE);
234*fe801747SSiva Chandra Reddy   }
235*fe801747SSiva Chandra Reddy 
236*fe801747SSiva Chandra Reddy   // If the thread was already detached, then the detach method should not
237*fe801747SSiva Chandra Reddy   // be called at all. If the thread is exiting, then we wait for it to exit
238*fe801747SSiva Chandra Reddy   // and free up resources.
239*fe801747SSiva Chandra Reddy   wait();
240*fe801747SSiva Chandra Reddy 
241*fe801747SSiva Chandra Reddy   if (attrib->owned_stack)
242*fe801747SSiva Chandra Reddy     free_stack(attrib->stack, attrib->stack_size);
243*fe801747SSiva Chandra Reddy   return int(DetachType::CLEANUP);
244*fe801747SSiva Chandra Reddy }
245*fe801747SSiva Chandra Reddy 
246*fe801747SSiva Chandra Reddy void Thread::wait() {
247*fe801747SSiva Chandra Reddy   // The kernel should set the value at the clear tid address to zero.
248*fe801747SSiva Chandra Reddy   // If not, it is a spurious wake and we should continue to wait on
249*fe801747SSiva Chandra Reddy   // the futex.
250*fe801747SSiva Chandra Reddy   auto *clear_tid =
251*fe801747SSiva Chandra Reddy       reinterpret_cast<cpp::Atomic<FutexWordType> *>(platform_data);
252*fe801747SSiva Chandra Reddy   while (clear_tid->load() != 0) {
253*fe801747SSiva Chandra Reddy     // We cannot do a FUTEX_WAIT_PRIVATE here as the kernel does a
254*fe801747SSiva Chandra Reddy     // FUTEX_WAKE and not a FUTEX_WAKE_PRIVATE.
255*fe801747SSiva Chandra Reddy     __llvm_libc::syscall(SYS_futex, &clear_tid->val, FUTEX_WAIT,
256*fe801747SSiva Chandra Reddy                          CLEAR_TID_VALUE, nullptr);
257*fe801747SSiva Chandra Reddy   }
258*fe801747SSiva Chandra Reddy }
259*fe801747SSiva Chandra Reddy 
260*fe801747SSiva Chandra Reddy } // namespace __llvm_libc
261