-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathos_thread.h
369 lines (304 loc) · 12.3 KB
/
os_thread.h
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_OS_THREAD_H_
#define RUNTIME_VM_OS_THREAD_H_
#include "platform/atomic.h"
#include "platform/globals.h"
#include "platform/safe_stack.h"
#include "platform/synchronization.h"
#include "platform/threads.h"
#include "platform/utils.h"
#include "vm/allocation.h"
#include "vm/globals.h"
// Declare the OS-specific types ahead of defining the generic classes.
#if defined(DART_USE_ABSL)
#include "vm/os_thread_absl.h"
#elif defined(DART_HOST_OS_ANDROID)
#include "vm/os_thread_android.h"
#elif defined(DART_HOST_OS_FUCHSIA)
#include "vm/os_thread_fuchsia.h"
#elif defined(DART_HOST_OS_LINUX)
#include "vm/os_thread_linux.h"
#elif defined(DART_HOST_OS_MACOS)
#include "vm/os_thread_macos.h"
#elif defined(DART_HOST_OS_WINDOWS)
#include "vm/os_thread_win.h"
#else
#error Unknown target os.
#endif
namespace dart {
// Forward declarations.
class Log;
class Mutex;
class ThreadState;
class TimelineEventBlock;
class BaseThread {
public:
bool is_os_thread() const { return is_os_thread_; }
private:
explicit BaseThread(bool is_os_thread) : is_os_thread_(is_os_thread) {}
virtual ~BaseThread() {}
bool is_os_thread_;
friend class ThreadState;
friend class OSThread;
DISALLOW_IMPLICIT_CONSTRUCTORS(BaseThread);
};
using ThreadId = platform::ThreadId;
// Low-level operations on OS platform threads.
class OSThread : public BaseThread {
public:
static const uword kInvalidStackLimit = ~static_cast<uword>(0);
// The constructor of OSThread is never called directly, instead we call
// this factory style method 'CreateOSThread' to create OSThread structures.
// The method can return a nullptr if the Dart VM is in shutdown mode.
static OSThread* CreateOSThread();
~OSThread();
ThreadId id() const {
ASSERT(id_ != OSThread::kInvalidThreadId);
return id_;
}
#ifdef SUPPORT_TIMELINE
ThreadId trace_id() const {
ASSERT(trace_id_ != OSThread::kInvalidThreadId);
return trace_id_;
}
#endif
const char* name() const { return name_; }
void SetName(const char* name);
Mutex* timeline_block_lock() const { return &timeline_block_lock_; }
// Only safe to access when holding |timeline_block_lock_|.
TimelineEventBlock* TimelineBlockLocked() const {
ASSERT(timeline_block_lock()->IsOwnedByCurrentThread());
return timeline_block_;
}
// Only safe to access when holding |timeline_block_lock_|.
void SetTimelineBlockLocked(TimelineEventBlock* block) {
ASSERT(timeline_block_lock()->IsOwnedByCurrentThread());
timeline_block_ = block;
}
Log* log() const { return log_; }
uword stack_base() const { return stack_base_; }
uword stack_limit() const { return stack_limit_; }
uword overflow_stack_limit() const { return stack_limit_ + stack_headroom_; }
bool HasStackHeadroom() { return HasStackHeadroom(stack_headroom_); }
bool HasStackHeadroom(intptr_t headroom) {
return GetCurrentStackPointer() > (stack_limit_ + headroom);
}
#ifdef SUPPORT_TIMELINE
static ThreadId GetCurrentThreadTraceId();
#endif // SUPPORT_TIMELINE
// May fail for the main thread on Linux if resources are low.
static bool GetCurrentStackBounds(uword* lower, uword* upper);
// Returns the current C++ stack pointer. Equivalent taking the address of a
// stack allocated local, but plays well with AddressSanitizer and SafeStack.
// Accurate enough for stack overflow checks but not accurate enough for
// alignment checks.
static uword GetCurrentStackPointer();
#if defined(USING_SAFE_STACK)
static uword GetCurrentSafestackPointer();
static void SetCurrentSafestackPointer(uword ssp);
#endif
#if !defined(PRODUCT)
// Used to temporarily disable or enable thread interrupts.
void DisableThreadInterrupts();
void EnableThreadInterrupts();
bool ThreadInterruptsEnabled();
#endif // !defined(PRODUCT)
// The currently executing thread, or nullptr if not yet initialized.
static OSThread* TryCurrent() {
BaseThread* thread = GetCurrentTLS();
OSThread* os_thread = nullptr;
if (thread != nullptr) {
if (thread->is_os_thread()) {
os_thread = reinterpret_cast<OSThread*>(thread);
} else {
ThreadState* vm_thread = reinterpret_cast<ThreadState*>(thread);
os_thread = GetOSThreadFromThread(vm_thread);
}
}
return os_thread;
}
// The currently executing thread. If there is no currently executing thread,
// a new OSThread is created and returned.
static OSThread* Current() {
OSThread* os_thread = TryCurrent();
if (os_thread == nullptr) {
os_thread = CreateAndSetUnknownThread();
}
return os_thread;
}
static void SetCurrent(OSThread* current) { SetCurrentTLS(current); }
static ThreadState* CurrentVMThread() { return current_vm_thread_; }
#if defined(DEBUG)
static void SetCurrentVMThread(ThreadState* thread) {
current_vm_thread_ = thread;
}
#endif
// TODO(5411455): Use flag to override default value and Validate the
// stack size by querying OS.
static uword GetSpecifiedStackSize() {
intptr_t headroom =
OSThread::CalculateHeadroom(OSThread::GetMaxStackSize());
ASSERT(headroom < OSThread::GetMaxStackSize());
uword stack_size = OSThread::GetMaxStackSize() - headroom;
return stack_size;
}
static BaseThread* GetCurrentTLS() {
return reinterpret_cast<BaseThread*>(OSThread::GetThreadLocal(thread_key_));
}
static void SetCurrentTLS(BaseThread* value);
typedef void (*ThreadStartFunction)(uword parameter);
typedef void (*ThreadDestructor)(void* parameter);
// Start a thread running the specified function. Returns 0 if the
// thread started successfully and a system specific error code if
// the thread failed to start.
DART_WARN_UNUSED_RESULT static int TryStart(const char* name,
ThreadStartFunction function,
uword parameter);
// Start a thread running the specified function. If the thread fails to
// start, then exit with an descriptive error message.
static void Start(const char* name,
ThreadStartFunction function,
uword parameter);
static ThreadLocalKey CreateThreadLocal(
ThreadDestructor destructor = nullptr);
static void DeleteThreadLocal(ThreadLocalKey key);
static uword GetThreadLocal(ThreadLocalKey key) {
return ThreadInlineImpl::GetThreadLocal(key);
}
static void SetThreadLocal(ThreadLocalKey key, uword value);
static intptr_t GetMaxStackSize();
static void Join(ThreadJoinId id);
static void Detach(ThreadJoinId id);
static intptr_t ThreadIdToIntPtr(ThreadId id);
static ThreadId ThreadIdFromIntPtr(intptr_t id);
// This function can be called only once per OSThread, and should only be
// called when the returned id will eventually be passed to OSThread::Join().
static ThreadJoinId GetCurrentThreadJoinId(OSThread* thread);
// Called at VM startup and shutdown.
static void Init();
static bool IsThreadInList(ThreadId id);
static void DisableOSThreadCreation();
static bool CanCreateOSThreads();
static void EnableOSThreadCreation();
static constexpr intptr_t kStackSizeBufferMax = (16 * KB * kWordSize);
static constexpr float kStackSizeBufferFraction = 0.5;
static const ThreadJoinId kInvalidThreadJoinId;
static constexpr ThreadId kInvalidThreadId = platform::kInvalidThreadId;
static ThreadId GetCurrentThreadId() {
return platform::GetCurrentThreadId();
}
static bool Compare(ThreadId a, ThreadId b) {
return platform::AreSameThreads(a, b);
}
private:
// The constructor is private as CreateOSThread should be used
// to create a new OSThread structure.
OSThread();
// These methods should not be used in a generic way and hence
// are private, they have been added to solve the problem of
// accessing the VM thread structure from an OSThread object
// in the windows thread interrupter which is used for profiling.
// We could eliminate this requirement if the windows thread interrupter
// is implemented differently.
ThreadState* thread() const { return thread_; }
void set_thread(ThreadState* value) { thread_ = value; }
static void Cleanup();
// Retrieves the name given to the current thread at the OS level and returns
// it as a heap-allocated string that must eventually be freed by the caller
// using free. Returns |nullptr| when the name cannot be retrieved.
static char* GetCurrentThreadName();
static OSThread* GetOSThreadFromThread(ThreadState* thread);
static void AddThreadToListLocked(OSThread* thread);
static void RemoveThreadFromList(OSThread* thread);
static OSThread* CreateAndSetUnknownThread();
static uword CalculateHeadroom(uword stack_size) {
uword headroom = kStackSizeBufferFraction * stack_size;
return (headroom > kStackSizeBufferMax) ? kStackSizeBufferMax : headroom;
}
static ThreadLocalKey thread_key_;
const ThreadId id_;
#if defined(DEBUG)
// In DEBUG mode we use this field to ensure that GetCurrentThreadJoinId is
// only called once per OSThread.
ThreadJoinId join_id_ = kInvalidThreadJoinId;
#endif
#ifdef SUPPORT_TIMELINE
const ThreadId trace_id_; // Used to interface with tracing tools.
#endif
char* name_; // A name for this thread.
mutable Mutex timeline_block_lock_;
// The block that the timeline recorder has permitted this thread to write
// events to.
TimelineEventBlock* timeline_block_ = nullptr;
// All |Thread|s are registered in the thread list.
OSThread* thread_list_next_ = nullptr;
#if !defined(PRODUCT)
// Thread interrupts disabled by default.
RelaxedAtomic<uintptr_t> thread_interrupt_disabled_ = {1};
bool prepared_for_interrupts_ = false;
void* thread_interrupter_state_ = nullptr;
#endif // !defined(PRODUCT)
Log* log_;
uword stack_base_ = 0;
uword stack_limit_ = 0;
uword stack_headroom_ = 0;
ThreadState* thread_ = nullptr;
// The ThreadPool::Worker which owns this OSThread. If this OSThread was not
// started by a ThreadPool it will be nullptr. This TLS value is not
// protected and should only be read/written by the OSThread itself.
void* owning_thread_pool_worker_ = nullptr;
// thread_list_lock_ cannot have a static lifetime because the order in which
// destructors run is undefined. At the moment this lock cannot be deleted
// either since otherwise, if a thread only begins to run after we have
// started to run TLS destructors for a call to exit(), there will be a race
// on its deletion in CreateOSThread().
static Mutex* thread_list_lock_;
static OSThread* thread_list_head_;
static bool creation_enabled_;
// Inline initialization is important for avoiding unnecessary TLS
// initialization checks at each use.
static inline thread_local ThreadState* current_vm_thread_ = nullptr;
friend class Thread; // to access set_thread(Thread*).
friend class OSThreadIterator;
friend class ThreadInterrupterFuchsia;
friend class ThreadInterrupterMacOS;
friend class ThreadInterrupterWin;
friend class ThreadPool; // to access owning_thread_pool_worker_
};
// Note that this takes the thread list lock, prohibiting threads from coming
// on- or off-line.
class OSThreadIterator : public ValueObject {
public:
OSThreadIterator();
~OSThreadIterator();
// Returns false when there are no more threads left.
bool HasNext() const;
// Returns the current thread and moves forward.
OSThread* Next();
private:
OSThread* next_;
};
// Mark when we are running in a signal handler (Linux, Android) or with a
// suspended thread (Windows, Mac, Fuchia). During this time, we cannot take
// locks, access Thread/Isolate::Current(), or use malloc.
class ThreadInterruptScope : public ValueObject {
#if defined(DEBUG)
public:
ThreadInterruptScope() {
// Poison attempts to use Thread::Current. This is much cheaper than adding
// an assert in Thread::Current itself.
saved_current_vm_thread_ = OSThread::CurrentVMThread();
OSThread::SetCurrentVMThread(reinterpret_cast<ThreadState*>(0xabababab));
}
~ThreadInterruptScope() {
OSThread::SetCurrentVMThread(saved_current_vm_thread_);
}
private:
DisallowMutexLockingScope disallow_locks_;
ThreadState* saved_current_vm_thread_;
#endif // DEBUG
};
} // namespace dart
#endif // RUNTIME_VM_OS_THREAD_H_