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