1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_OS_THREAD_H_
6#define RUNTIME_VM_OS_THREAD_H_
7
8#include "platform/atomic.h"
9#include "platform/globals.h"
10#include "platform/safe_stack.h"
11#include "platform/utils.h"
12#include "vm/allocation.h"
13#include "vm/globals.h"
14
15// Declare the OS-specific types ahead of defining the generic classes.
16#if defined(DART_USE_ABSL)
17#include "vm/os_thread_absl.h"
18#elif defined(DART_HOST_OS_ANDROID)
19#include "vm/os_thread_android.h"
20#elif defined(DART_HOST_OS_FUCHSIA)
21#include "vm/os_thread_fuchsia.h"
22#elif defined(DART_HOST_OS_LINUX)
23#include "vm/os_thread_linux.h"
24#elif defined(DART_HOST_OS_MACOS)
25#include "vm/os_thread_macos.h"
26#elif defined(DART_HOST_OS_WINDOWS)
27#include "vm/os_thread_win.h"
28#else
29#error Unknown target os.
30#endif
31
32namespace dart {
33
34// Forward declarations.
35class Log;
36class Mutex;
37class ThreadState;
38class TimelineEventBlock;
39
40class Mutex {
41 public:
42 explicit Mutex(NOT_IN_PRODUCT(const char* name = "anonymous mutex"));
43 ~Mutex();
44
45 bool IsOwnedByCurrentThread() const;
46
47 private:
48 void Lock();
49 bool TryLock(); // Returns false if lock is busy and locking failed.
50 void Unlock();
51
52 MutexData data_;
53 NOT_IN_PRODUCT(const char* name_);
54#if defined(DEBUG)
55 ThreadId owner_;
56#endif // defined(DEBUG)
57
58 friend class MallocLocker;
59 friend class MutexLocker;
60 friend class SafepointMutexLocker;
61 friend class OSThreadIterator;
62 friend class TimelineEventRecorder;
63 friend class TimelineEventRingRecorder;
64 friend class PageSpace;
65 friend void Dart_TestMutex();
66 DISALLOW_COPY_AND_ASSIGN(Mutex);
67};
68
69class BaseThread {
70 public:
71 bool is_os_thread() const { return is_os_thread_; }
72
73 private:
74 explicit BaseThread(bool is_os_thread) : is_os_thread_(is_os_thread) {}
75 virtual ~BaseThread() {}
76
77 bool is_os_thread_;
78
79 friend class ThreadState;
80 friend class OSThread;
81
82 DISALLOW_IMPLICIT_CONSTRUCTORS(BaseThread);
83};
84
85// Low-level operations on OS platform threads.
86class OSThread : public BaseThread {
87 public:
88 static const uword kInvalidStackLimit = ~static_cast<uword>(0);
89
90 // The constructor of OSThread is never called directly, instead we call
91 // this factory style method 'CreateOSThread' to create OSThread structures.
92 // The method can return a nullptr if the Dart VM is in shutdown mode.
93 static OSThread* CreateOSThread();
94 ~OSThread();
95
96 ThreadId id() const {
97 ASSERT(id_ != OSThread::kInvalidThreadId);
98 return id_;
99 }
100
101#ifdef SUPPORT_TIMELINE
102 ThreadId trace_id() const {
103 ASSERT(trace_id_ != OSThread::kInvalidThreadId);
104 return trace_id_;
105 }
106#endif
107
108 const char* name() const { return name_; }
109
110 void SetName(const char* name);
111
112 Mutex* timeline_block_lock() const { return &timeline_block_lock_; }
113
114 // Only safe to access when holding |timeline_block_lock_|.
115 TimelineEventBlock* TimelineBlockLocked() const {
116 ASSERT(timeline_block_lock()->IsOwnedByCurrentThread());
117 return timeline_block_;
118 }
119
120 // Only safe to access when holding |timeline_block_lock_|.
121 void SetTimelineBlockLocked(TimelineEventBlock* block) {
122 ASSERT(timeline_block_lock()->IsOwnedByCurrentThread());
123 timeline_block_ = block;
124 }
125
126 Log* log() const { return log_; }
127
128 uword stack_base() const { return stack_base_; }
129 uword stack_limit() const { return stack_limit_; }
130 uword overflow_stack_limit() const { return stack_limit_ + stack_headroom_; }
131
132 bool HasStackHeadroom() { return HasStackHeadroom(headroom: stack_headroom_); }
133 bool HasStackHeadroom(intptr_t headroom) {
134 return GetCurrentStackPointer() > (stack_limit_ + headroom);
135 }
136
137 // May fail for the main thread on Linux if resources are low.
138 static bool GetCurrentStackBounds(uword* lower, uword* upper);
139
140 // Returns the current C++ stack pointer. Equivalent taking the address of a
141 // stack allocated local, but plays well with AddressSanitizer and SafeStack.
142 // Accurate enough for stack overflow checks but not accurate enough for
143 // alignment checks.
144 static uword GetCurrentStackPointer();
145
146#if defined(USING_SAFE_STACK)
147 static uword GetCurrentSafestackPointer();
148 static void SetCurrentSafestackPointer(uword ssp);
149#endif
150
151 // Used to temporarily disable or enable thread interrupts.
152 void DisableThreadInterrupts();
153 void EnableThreadInterrupts();
154 bool ThreadInterruptsEnabled();
155
156 // The currently executing thread, or nullptr if not yet initialized.
157 static OSThread* TryCurrent() {
158 BaseThread* thread = GetCurrentTLS();
159 OSThread* os_thread = nullptr;
160 if (thread != nullptr) {
161 if (thread->is_os_thread()) {
162 os_thread = reinterpret_cast<OSThread*>(thread);
163 } else {
164 ThreadState* vm_thread = reinterpret_cast<ThreadState*>(thread);
165 os_thread = GetOSThreadFromThread(thread: vm_thread);
166 }
167 }
168 return os_thread;
169 }
170
171 // The currently executing thread. If there is no currently executing thread,
172 // a new OSThread is created and returned.
173 static OSThread* Current() {
174 OSThread* os_thread = TryCurrent();
175 if (os_thread == nullptr) {
176 os_thread = CreateAndSetUnknownThread();
177 }
178 return os_thread;
179 }
180 static void SetCurrent(OSThread* current) { SetCurrentTLS(current); }
181
182 static ThreadState* CurrentVMThread() { return current_vm_thread_; }
183#if defined(DEBUG)
184 static void SetCurrentVMThread(ThreadState* thread) {
185 current_vm_thread_ = thread;
186 }
187#endif
188
189 // TODO(5411455): Use flag to override default value and Validate the
190 // stack size by querying OS.
191 static uword GetSpecifiedStackSize() {
192 intptr_t headroom =
193 OSThread::CalculateHeadroom(stack_size: OSThread::GetMaxStackSize());
194 ASSERT(headroom < OSThread::GetMaxStackSize());
195 uword stack_size = OSThread::GetMaxStackSize() - headroom;
196 return stack_size;
197 }
198 static BaseThread* GetCurrentTLS() {
199 return reinterpret_cast<BaseThread*>(OSThread::GetThreadLocal(key: thread_key_));
200 }
201 static void SetCurrentTLS(BaseThread* value);
202
203 typedef void (*ThreadStartFunction)(uword parameter);
204 typedef void (*ThreadDestructor)(void* parameter);
205
206 // Start a thread running the specified function. Returns 0 if the
207 // thread started successfully and a system specific error code if
208 // the thread failed to start.
209 static int Start(const char* name,
210 ThreadStartFunction function,
211 uword parameter);
212
213 static ThreadLocalKey CreateThreadLocal(
214 ThreadDestructor destructor = nullptr);
215 static void DeleteThreadLocal(ThreadLocalKey key);
216 static uword GetThreadLocal(ThreadLocalKey key) {
217 return ThreadInlineImpl::GetThreadLocal(key);
218 }
219 static ThreadId GetCurrentThreadId();
220 static void SetThreadLocal(ThreadLocalKey key, uword value);
221 static intptr_t GetMaxStackSize();
222 static void Join(ThreadJoinId id);
223 static intptr_t ThreadIdToIntPtr(ThreadId id);
224 static ThreadId ThreadIdFromIntPtr(intptr_t id);
225 static bool Compare(ThreadId a, ThreadId b);
226
227 // This function can be called only once per OSThread, and should only be
228 // called when the returned id will eventually be passed to OSThread::Join().
229 static ThreadJoinId GetCurrentThreadJoinId(OSThread* thread);
230
231 // Called at VM startup and shutdown.
232 static void Init();
233
234 static bool IsThreadInList(ThreadId id);
235
236 static void DisableOSThreadCreation();
237 static void EnableOSThreadCreation();
238
239 static constexpr intptr_t kStackSizeBufferMax = (16 * KB * kWordSize);
240 static constexpr float kStackSizeBufferFraction = 0.5;
241
242 static const ThreadId kInvalidThreadId;
243 static const ThreadJoinId kInvalidThreadJoinId;
244
245 private:
246 // The constructor is private as CreateOSThread should be used
247 // to create a new OSThread structure.
248 OSThread();
249
250 // These methods should not be used in a generic way and hence
251 // are private, they have been added to solve the problem of
252 // accessing the VM thread structure from an OSThread object
253 // in the windows thread interrupter which is used for profiling.
254 // We could eliminate this requirement if the windows thread interrupter
255 // is implemented differently.
256 ThreadState* thread() const { return thread_; }
257 void set_thread(ThreadState* value) { thread_ = value; }
258
259 static void Cleanup();
260#ifdef SUPPORT_TIMELINE
261 static ThreadId GetCurrentThreadTraceId();
262#endif // SUPPORT_TIMELINE
263
264 // Retrieves the name given to the current thread at the OS level and returns
265 // it as a heap-allocated string that must eventually be freed by the caller
266 // using free. Returns |nullptr| when the name cannot be retrieved.
267 static char* GetCurrentThreadName();
268 static OSThread* GetOSThreadFromThread(ThreadState* thread);
269 static void AddThreadToListLocked(OSThread* thread);
270 static void RemoveThreadFromList(OSThread* thread);
271 static OSThread* CreateAndSetUnknownThread();
272
273 static uword CalculateHeadroom(uword stack_size) {
274 uword headroom = kStackSizeBufferFraction * stack_size;
275 return (headroom > kStackSizeBufferMax) ? kStackSizeBufferMax : headroom;
276 }
277
278 static ThreadLocalKey thread_key_;
279
280 const ThreadId id_;
281#if defined(DEBUG)
282 // In DEBUG mode we use this field to ensure that GetCurrentThreadJoinId is
283 // only called once per OSThread.
284 ThreadJoinId join_id_;
285#endif
286#ifdef SUPPORT_TIMELINE
287 const ThreadId trace_id_; // Used to interface with tracing tools.
288#endif
289 char* name_; // A name for this thread.
290
291 mutable Mutex timeline_block_lock_;
292 // The block that the timeline recorder has permitted this thread to write
293 // events to.
294 TimelineEventBlock* timeline_block_;
295
296 // All |Thread|s are registered in the thread list.
297 OSThread* thread_list_next_;
298
299 RelaxedAtomic<uintptr_t> thread_interrupt_disabled_;
300 Log* log_;
301 uword stack_base_;
302 uword stack_limit_;
303 uword stack_headroom_;
304 ThreadState* thread_;
305 // The ThreadPool::Worker which owns this OSThread. If this OSThread was not
306 // started by a ThreadPool it will be nullptr. This TLS value is not
307 // protected and should only be read/written by the OSThread itself.
308 void* owning_thread_pool_worker_ = nullptr;
309
310 // thread_list_lock_ cannot have a static lifetime because the order in which
311 // destructors run is undefined. At the moment this lock cannot be deleted
312 // either since otherwise, if a thread only begins to run after we have
313 // started to run TLS destructors for a call to exit(), there will be a race
314 // on its deletion in CreateOSThread().
315 static Mutex* thread_list_lock_;
316 static OSThread* thread_list_head_;
317 static bool creation_enabled_;
318
319 // Inline initialization is important for avoiding unnecessary TLS
320 // initialization checks at each use.
321 static inline thread_local ThreadState* current_vm_thread_ = nullptr;
322
323 friend class Thread; // to access set_thread(Thread*).
324 friend class OSThreadIterator;
325 friend class ThreadInterrupterFuchsia;
326 friend class ThreadInterrupterMacOS;
327 friend class ThreadInterrupterWin;
328 friend class ThreadPool; // to access owning_thread_pool_worker_
329};
330
331// Note that this takes the thread list lock, prohibiting threads from coming
332// on- or off-line.
333class OSThreadIterator : public ValueObject {
334 public:
335 OSThreadIterator();
336 ~OSThreadIterator();
337
338 // Returns false when there are no more threads left.
339 bool HasNext() const;
340
341 // Returns the current thread and moves forward.
342 OSThread* Next();
343
344 private:
345 OSThread* next_;
346};
347
348class Monitor {
349 public:
350 enum WaitResult { kNotified, kTimedOut };
351
352 static constexpr int64_t kNoTimeout = 0;
353
354 Monitor();
355 ~Monitor();
356
357#if defined(DEBUG)
358 bool IsOwnedByCurrentThread() const {
359 return owner_ == OSThread::GetCurrentThreadId();
360 }
361#else
362 bool IsOwnedByCurrentThread() const {
363 UNREACHABLE();
364 return false;
365 }
366#endif
367
368 private:
369 bool TryEnter(); // Returns false if lock is busy and locking failed.
370 void Enter();
371 void Exit();
372
373 // Wait for notification or timeout.
374 WaitResult Wait(int64_t millis);
375 WaitResult WaitMicros(int64_t micros);
376
377 // Notify waiting threads.
378 void Notify();
379 void NotifyAll();
380
381 MonitorData data_; // OS-specific data.
382#if defined(DEBUG)
383 ThreadId owner_;
384#endif // defined(DEBUG)
385
386 friend class MonitorLocker;
387 friend class SafepointMonitorLocker;
388 friend class SafepointRwLock;
389 friend void Dart_TestMonitor();
390 DISALLOW_COPY_AND_ASSIGN(Monitor);
391};
392
393inline bool Mutex::IsOwnedByCurrentThread() const {
394#if defined(DEBUG)
395 return owner_ == OSThread::GetCurrentThreadId();
396#else
397 UNREACHABLE();
398 return false;
399#endif
400}
401
402// Mark when we are running in a signal handler (Linux, Android) or with a
403// suspended thread (Windows, Mac, Fuchia). During this time, we cannot take
404// locks, access Thread/Isolate::Current(), or use malloc.
405class ThreadInterruptScope : public ValueObject {
406#if defined(DEBUG)
407 public:
408 ThreadInterruptScope() {
409 ASSERT(!in_thread_interrupt_scope_); // We don't use nested signals.
410 in_thread_interrupt_scope_ = true;
411
412 // Poison attempts to use Thread::Current. This is much cheaper than adding
413 // an assert in Thread::Current itself.
414 saved_current_vm_thread_ = OSThread::CurrentVMThread();
415 OSThread::SetCurrentVMThread(reinterpret_cast<ThreadState*>(0xabababab));
416 }
417
418 ~ThreadInterruptScope() {
419 OSThread::SetCurrentVMThread(saved_current_vm_thread_);
420 in_thread_interrupt_scope_ = false;
421 }
422
423 static bool in_thread_interrupt_scope() { return in_thread_interrupt_scope_; }
424
425 private:
426 ThreadState* saved_current_vm_thread_;
427 static inline thread_local bool in_thread_interrupt_scope_ = false;
428#endif // DEBUG
429};
430
431} // namespace dart
432
433#endif // RUNTIME_VM_OS_THREAD_H_
434

source code of flutter_engine/third_party/dart/runtime/vm/os_thread.h