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