summaryrefslogtreecommitdiff
path: root/libc/src/__support/threads/thread.h
blob: e0b644d963872182f6b39fa08cb1033352e386c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//===--- A platform independent indirection for a thread class --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H

#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/stringstream.h"
#include "src/__support/macros/properties/architectures.h"

#include <stddef.h> // For size_t
#include <stdint.h>

namespace __llvm_libc {

using ThreadRunnerPosix = void *(void *);
using ThreadRunnerStdc = int(void *);

union ThreadRunner {
  ThreadRunnerPosix *posix_runner;
  ThreadRunnerStdc *stdc_runner;
};

union ThreadReturnValue {
  void *posix_retval;
  int stdc_retval;
  constexpr ThreadReturnValue() : posix_retval(nullptr) {}
  constexpr ThreadReturnValue(int r) : stdc_retval(r) {}
  constexpr ThreadReturnValue(void *r) : posix_retval(r) {}
};

#if (defined(LIBC_TARGET_ARCH_IS_AARCH64) ||                                   \
     defined(LIBC_TARGET_ARCH_IS_X86_64) ||                                    \
     defined(LIBC_TARGET_ARCH_IS_RISCV64))
constexpr unsigned int STACK_ALIGNMENT = 16;
#endif
// TODO: Provide stack alignment requirements for other architectures.

enum class DetachState : uint32_t {
  JOINABLE = 0x11,
  EXITING = 0x22,
  DETACHED = 0x33
};

enum class ThreadStyle : uint8_t { POSIX = 0x1, STDC = 0x2 };

// Detach type is useful in testing the detach operation.
enum class DetachType : int {
  // Indicates that the detach operation just set the detach state to DETACHED
  // and returned.
  SIMPLE = 1,

  // Indicates that the detach operation performed thread cleanup.
  CLEANUP = 2
};

class ThreadAtExitCallbackMgr;

// A data type to hold common thread attributes which have to be stored as
// thread state. Note that this is different from public attribute types like
// pthread_attr_t which might contain information which need not be saved as
// part of a thread's state. For example, the stack guard size.
//
// Thread attributes are typically stored on the stack. So, we align as required
// for the target architecture.
struct alignas(STACK_ALIGNMENT) ThreadAttributes {
  // We want the "detach_state" attribute to be an atomic value as it could be
  // updated by one thread while the self thread is reading it. It is a tristate
  // variable with the following state transitions:
  // 1. The a thread is created in a detached state, then user code should never
  //    call a detach or join function. Calling either of them can lead to
  //    undefined behavior.
  //    The value of |detach_state| is expected to be DetachState::DETACHED for
  //    its lifetime.
  // 2. If a thread is created in a joinable state, |detach_state| will start
  //    with the value DetachState::JOINABLE. Another thread can detach this
  //    thread before it exits. The state transitions will as follows:
  //      (a) If the detach method sees the state as JOINABLE, then it will
  //          compare exchange to a state of DETACHED. The thread will clean
  //          itself up after it finishes.
  //      (b) If the detach method does not see JOINABLE in (a), then it will
  //          conclude that the thread is EXITING and will wait until the thread
  //          exits. It will clean up the thread resources once the thread
  //          exits.
  cpp::Atomic<uint32_t> detach_state;
  void *stack;                   // Pointer to the thread stack
  unsigned long long stack_size; // Size of the stack
  uintptr_t tls;                 // Address to the thread TLS memory
  uintptr_t tls_size;            // The size of area pointed to by |tls|.
  unsigned char owned_stack; // Indicates if the thread owns this stack memory
  int tid;
  ThreadStyle style;
  ThreadReturnValue retval;
  ThreadAtExitCallbackMgr *atexit_callback_mgr;
  void *platform_data;

  constexpr ThreadAttributes()
      : detach_state(uint32_t(DetachState::DETACHED)), stack(nullptr),
        stack_size(0), tls(0), tls_size(0), owned_stack(false), tid(-1),
        style(ThreadStyle::POSIX), retval(), atexit_callback_mgr(nullptr),
        platform_data(nullptr) {}
};

using TSSDtor = void(void *);

// Create a new TSS key and associate the |dtor| as the corresponding
// destructor. Can be used to implement public functions like
// pthread_key_create.
cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor);

// Delete the |key|. Can be used to implement public functions like
// pthread_key_delete.
//
// Return true on success, false on failure.
bool tss_key_delete(unsigned int key);

// Set the value associated with |key| for the current thread. Can be used
// to implement public functions like pthread_setspecific.
//
// Return true on success, false on failure.
bool set_tss_value(unsigned int key, void *value);

// Return the value associated with |key| for the current thread. Return
// nullptr if |key| is invalid. Can be used to implement public functions like
// pthread_getspecific.
void *get_tss_value(unsigned int key);

struct Thread {
  ThreadAttributes *attrib;

  constexpr Thread() : attrib(nullptr) {}
  constexpr Thread(ThreadAttributes *attr) : attrib(attr) {}

  int run(ThreadRunnerPosix *func, void *arg, void *stack, size_t size,
          bool detached = false) {
    ThreadRunner runner;
    runner.posix_runner = func;
    return run(ThreadStyle::POSIX, runner, arg, stack, size, detached);
  }

  int run(ThreadRunnerStdc *func, void *arg, void *stack, size_t size,
          bool detached = false) {
    ThreadRunner runner;
    runner.stdc_runner = func;
    return run(ThreadStyle::STDC, runner, arg, stack, size, detached);
  }

  int join(int *val) {
    ThreadReturnValue retval;
    int status = join(retval);
    if (status != 0)
      return status;
    if (val != nullptr)
      *val = retval.stdc_retval;
    return 0;
  }

  int join(void **val) {
    ThreadReturnValue retval;
    int status = join(retval);
    if (status != 0)
      return status;
    if (val != nullptr)
      *val = retval.posix_retval;
    return 0;
  }

  // Platform should implement the functions below.

  // Return 0 on success or an error value on failure.
  int run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
          size_t stack_size, bool detached);

  // Return 0 on success or an error value on failure.
  int join(ThreadReturnValue &retval);

  // Detach a joinable thread.
  //
  // This method does not have error return value. However, the type of detach
  // is returned to help with testing.
  int detach();

  // Wait for the thread to finish. This method can only be called
  // if:
  // 1. A detached thread is guaranteed to be running.
  // 2. A joinable thread has not been detached or joined. As long as it has
  //    not been detached or joined, wait can be called multiple times.
  //
  // Also, only one thread can wait and expect to get woken up when the thread
  // finishes.
  //
  // NOTE: This function is to be used for testing only. There is no standard
  // which requires exposing it via a public API.
  void wait();

  // Return true if this thread is equal to the other thread.
  bool operator==(const Thread &other) const;

  // Set the name of the thread. Return the error number on error.
  int set_name(const cpp::string_view &name);

  // Return the name of the thread in |name|. Return the error number of error.
  int get_name(cpp::StringStream &name) const;
};

extern thread_local Thread self;

// Platforms should implement this function.
void thread_exit(ThreadReturnValue retval, ThreadStyle style);

namespace internal {
// Internal namespace containing utilities which are to be used by platform
// implementations of threads.

// Return the current thread's atexit callback manager. After thread startup
// but before running the thread function, platform implementations should
// set the "atexit_callback_mgr" field of the thread's attributes to the value
// returned by this function.
ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr();

// Call the currently registered thread specific atexit callbacks. Useful for
// implementing the thread_exit function.
void call_atexit_callbacks(ThreadAttributes *attrib);

} // namespace internal

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H