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;
ThreadReturnValue()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.
alignas(STACK_ALIGNMENT)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 
ThreadThread103   constexpr Thread() : attrib(nullptr) {}
ThreadThread104   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 
joinThread120   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 
joinThread129   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