Flutter Engine
The Flutter Engine
os_thread_macos.cc
Go to the documentation of this file.
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#include "platform/globals.h" // NOLINT
6#if defined(DART_HOST_OS_MACOS) && !defined(DART_USE_ABSL)
7
8#include "vm/os_thread.h"
9
10#include <mach/mach_host.h> // NOLINT
11#include <mach/mach_init.h> // NOLINT
12#include <mach/mach_port.h> // NOLINT
13#include <mach/mach_traps.h> // NOLINT
14#include <mach/task_info.h> // NOLINT
15#include <mach/thread_act.h> // NOLINT
16#include <mach/thread_info.h> // NOLINT
17#include <signal.h> // NOLINT
18#include <sys/errno.h> // NOLINT
19#include <sys/sysctl.h> // NOLINT
20#include <sys/types.h> // NOLINT
21
23#include "platform/assert.h"
24#include "platform/safe_stack.h"
26#include "platform/utils.h"
27
28#include "vm/flags.h"
29
30namespace dart {
31
32DEFINE_FLAG(int,
33 worker_thread_priority,
34 kMinInt,
35 "The thread priority the VM should use for new worker threads.");
36
37#define VALIDATE_PTHREAD_RESULT(result) \
38 if (result != 0) { \
39 const int kBufferSize = 1024; \
40 char error_message[kBufferSize]; \
41 Utils::StrError(result, error_message, kBufferSize); \
42 FATAL("pthread error: %d (%s)", result, error_message); \
43 }
44
45#if defined(PRODUCT)
46#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result)
47#else
48#define VALIDATE_PTHREAD_RESULT_NAMED(result) \
49 if (result != 0) { \
50 const int kBufferSize = 1024; \
51 char error_message[kBufferSize]; \
52 Utils::StrError(result, error_message, kBufferSize); \
53 FATAL("[%s] pthread error: %d (%s)", name_, result, error_message); \
54 }
55#endif
56
57#if defined(DEBUG)
58#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result)
59#else
60// NOTE: This (currently) expands to a no-op.
61#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0)
62#endif
63
64#ifdef DEBUG
65#define RETURN_ON_PTHREAD_FAILURE(result) \
66 if (result != 0) { \
67 const int kBufferSize = 1024; \
68 char error_message[kBufferSize]; \
69 Utils::StrError(result, error_message, kBufferSize); \
70 fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
71 result, error_message); \
72 return result; \
73 }
74#else
75#define RETURN_ON_PTHREAD_FAILURE(result) \
76 if (result != 0) return result;
77#endif
78
79class ThreadStartData {
80 public:
81 ThreadStartData(const char* name,
83 uword parameter)
84 : name_(name), function_(function), parameter_(parameter) {}
85
86 const char* name() const { return name_; }
87 OSThread::ThreadStartFunction function() const { return function_; }
88 uword parameter() const { return parameter_; }
89
90 private:
91 const char* name_;
93 uword parameter_;
94
95 DISALLOW_COPY_AND_ASSIGN(ThreadStartData);
96};
97
98// Dispatch to the thread start function provided by the caller. This trampoline
99// is used to ensure that the thread is properly destroyed if the thread just
100// exits.
101static void* ThreadStart(void* data_ptr) {
102 if (FLAG_worker_thread_priority != kMinInt) {
103 const pthread_t thread = pthread_self();
104 int policy = SCHED_FIFO;
105 struct sched_param schedule;
106 if (pthread_getschedparam(thread, &policy, &schedule) != 0) {
107 FATAL("Obtaining sched param failed: errno = %d\n", errno);
108 }
109 schedule.sched_priority = FLAG_worker_thread_priority;
110 if (pthread_setschedparam(thread, policy, &schedule) != 0) {
111 FATAL("Setting thread priority to %d failed: errno = %d\n",
112 FLAG_worker_thread_priority, errno);
113 }
114 }
115
116 ThreadStartData* data = reinterpret_cast<ThreadStartData*>(data_ptr);
117
118 const char* name = data->name();
120 uword parameter = data->parameter();
121 delete data;
122
123 // Set the thread name. We need to impose a limit on the name length so that
124 // we can know how large of a buffer to use when retrieving the name. We
125 // truncate the name at 16 bytes to be consistent with Android and Linux.
126 char truncated_name[16];
127 snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name);
128 pthread_setname_np(name);
129
130 // Create new OSThread object and set as TLS for new thread.
131 OSThread* thread = OSThread::CreateOSThread();
132 if (thread != nullptr) {
133 OSThread::SetCurrent(thread);
134 thread->SetName(name);
135 // Call the supplied thread start function handing it its parameters.
136 function(parameter);
137 }
138
139 return nullptr;
140}
141
142int OSThread::Start(const char* name,
143 ThreadStartFunction function,
144 uword parameter) {
145 pthread_attr_t attr;
146 int result = pthread_attr_init(&attr);
147 RETURN_ON_PTHREAD_FAILURE(result);
148
149 result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize());
150 RETURN_ON_PTHREAD_FAILURE(result);
151
152 ThreadStartData* data = new ThreadStartData(name, function, parameter);
153
154 pthread_t tid;
155 result = pthread_create(&tid, &attr, ThreadStart, data);
156 RETURN_ON_PTHREAD_FAILURE(result);
157
158 result = pthread_attr_destroy(&attr);
159 RETURN_ON_PTHREAD_FAILURE(result);
160
161 return 0;
162}
163
164const ThreadId OSThread::kInvalidThreadId = static_cast<ThreadId>(nullptr);
166 static_cast<ThreadJoinId>(nullptr);
167
169 pthread_key_t key = kUnsetThreadLocalKey;
170 int result = pthread_key_create(&key, destructor);
171 VALIDATE_PTHREAD_RESULT(result);
173 return key;
174}
175
178 int result = pthread_key_delete(key);
179 VALIDATE_PTHREAD_RESULT(result);
180}
181
184 int result = pthread_setspecific(key, reinterpret_cast<void*>(value));
185 VALIDATE_PTHREAD_RESULT(result);
186}
187
188intptr_t OSThread::GetMaxStackSize() {
189 const int kStackSize = (128 * kWordSize * KB);
190 return kStackSize;
191}
192
194 return pthread_self();
195}
196
197#ifdef SUPPORT_TIMELINE
198ThreadId OSThread::GetCurrentThreadTraceId() {
199 return ThreadIdFromIntPtr(pthread_mach_thread_np(pthread_self()));
200}
201#endif // SUPPORT_TIMELINE
202
203char* OSThread::GetCurrentThreadName() {
204 const intptr_t kNameBufferSize = 16;
205 char* name = static_cast<char*>(malloc(kNameBufferSize));
206 pthread_getname_np(pthread_self(), name, kNameBufferSize);
207 return name;
208}
209
211 ASSERT(thread != nullptr);
212 // Make sure we're filling in the join id for the current thread.
213 ASSERT(thread->id() == GetCurrentThreadId());
214 // Make sure the join_id_ hasn't been set, yet.
215 DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
216 pthread_t id = pthread_self();
217#if defined(DEBUG)
218 thread->join_id_ = id;
219#endif
220 return id;
221}
222
224 int result = pthread_join(id, nullptr);
225 ASSERT(result == 0);
226}
227
229 COMPILE_ASSERT(sizeof(id) <= sizeof(intptr_t));
230 return reinterpret_cast<intptr_t>(id);
231}
232
234 return reinterpret_cast<ThreadId>(id);
235}
236
238 return pthread_equal(a, b) != 0;
239}
240
242 *upper = reinterpret_cast<uword>(pthread_get_stackaddr_np(pthread_self()));
243 *lower = *upper - pthread_get_stacksize_np(pthread_self());
244 return true;
245}
246
247#if defined(USING_SAFE_STACK)
250uword OSThread::GetCurrentSafestackPointer() {
251#error "SAFE_STACK is unsupported on this platform"
252 return 0;
253}
254
257void OSThread::SetCurrentSafestackPointer(uword ssp) {
258#error "SAFE_STACK is unsupported on this platform"
259}
260#endif
261
262Mutex::Mutex(NOT_IN_PRODUCT(const char* name))
263#if !defined(PRODUCT)
264 : name_(name)
265#endif
266{
267 pthread_mutexattr_t attr;
268 int result = pthread_mutexattr_init(&attr);
269 VALIDATE_PTHREAD_RESULT_NAMED(result);
270
271#if defined(DEBUG)
272 result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
273 VALIDATE_PTHREAD_RESULT_NAMED(result);
274#endif // defined(DEBUG)
275
276 result = pthread_mutex_init(data_.mutex(), &attr);
277 // Verify that creating a pthread_mutex succeeded.
278 VALIDATE_PTHREAD_RESULT_NAMED(result);
279
280 result = pthread_mutexattr_destroy(&attr);
281 VALIDATE_PTHREAD_RESULT_NAMED(result);
282
283#if defined(DEBUG)
284 // When running with assertions enabled we do track the owner.
286#endif // defined(DEBUG)
287}
288
290 int result = pthread_mutex_destroy(data_.mutex());
291 // Verify that the pthread_mutex was destroyed.
292 VALIDATE_PTHREAD_RESULT_NAMED(result);
293
294#if defined(DEBUG)
295 // When running with assertions enabled we do track the owner.
297#endif // defined(DEBUG)
298}
299
300void Mutex::Lock() {
301 DEBUG_ASSERT(!ThreadInterruptScope::in_thread_interrupt_scope());
302
303 int result = pthread_mutex_lock(data_.mutex());
304 // Specifically check for dead lock to help debugging.
305 ASSERT(result != EDEADLK);
306 ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
307#if defined(DEBUG)
308 // When running with assertions enabled we do track the owner.
310#endif // defined(DEBUG)
311}
312
313bool Mutex::TryLock() {
314 DEBUG_ASSERT(!ThreadInterruptScope::in_thread_interrupt_scope());
315
316 int result = pthread_mutex_trylock(data_.mutex());
317 // Return false if the lock is busy and locking failed.
318 if ((result == EBUSY) || (result == EDEADLK)) {
319 return false;
320 }
321 ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
322#if defined(DEBUG)
323 // When running with assertions enabled we do track the owner.
325#endif // defined(DEBUG)
326 return true;
327}
328
329void Mutex::Unlock() {
330#if defined(DEBUG)
331 // When running with assertions enabled we do track the owner.
334#endif // defined(DEBUG)
335 int result = pthread_mutex_unlock(data_.mutex());
336 // Specifically check for wrong thread unlocking to aid debugging.
337 ASSERT(result != EPERM);
338 ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
339}
340
342 pthread_mutexattr_t attr;
343 int result = pthread_mutexattr_init(&attr);
344 VALIDATE_PTHREAD_RESULT(result);
345
346#if defined(DEBUG)
347 result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
348 VALIDATE_PTHREAD_RESULT(result);
349#endif // defined(DEBUG)
350
351 result = pthread_mutex_init(data_.mutex(), &attr);
352 VALIDATE_PTHREAD_RESULT(result);
353
354 result = pthread_mutexattr_destroy(&attr);
355 VALIDATE_PTHREAD_RESULT(result);
356
357 result = pthread_cond_init(data_.cond(), nullptr);
358 VALIDATE_PTHREAD_RESULT(result);
359
360#if defined(DEBUG)
361 // When running with assertions enabled we track the owner.
363#endif // defined(DEBUG)
364}
365
367#if defined(DEBUG)
368 // When running with assertions enabled we track the owner.
370#endif // defined(DEBUG)
371
372 int result = pthread_mutex_destroy(data_.mutex());
373 VALIDATE_PTHREAD_RESULT(result);
374
375 result = pthread_cond_destroy(data_.cond());
376 VALIDATE_PTHREAD_RESULT(result);
377}
378
379bool Monitor::TryEnter() {
380 DEBUG_ASSERT(!ThreadInterruptScope::in_thread_interrupt_scope());
381
382 int result = pthread_mutex_trylock(data_.mutex());
383 // Return false if the lock is busy and locking failed.
384 if ((result == EBUSY) || (result == EDEADLK)) {
385 return false;
386 }
387 ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors.
388#if defined(DEBUG)
389 // When running with assertions enabled we track the owner.
392#endif // defined(DEBUG)
393 return true;
394}
395
396void Monitor::Enter() {
397 DEBUG_ASSERT(!ThreadInterruptScope::in_thread_interrupt_scope());
398
399 int result = pthread_mutex_lock(data_.mutex());
400 VALIDATE_PTHREAD_RESULT(result);
401
402#if defined(DEBUG)
403 // When running with assertions enabled we track the owner.
406#endif // defined(DEBUG)
407}
408
409void Monitor::Exit() {
410#if defined(DEBUG)
411 // When running with assertions enabled we track the owner.
414#endif // defined(DEBUG)
415
416 int result = pthread_mutex_unlock(data_.mutex());
417 VALIDATE_PTHREAD_RESULT(result);
418}
419
420Monitor::WaitResult Monitor::Wait(int64_t millis) {
421 return WaitMicros(millis * kMicrosecondsPerMillisecond);
422}
423
424Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
425#if defined(DEBUG)
426 // When running with assertions enabled we track the owner.
428 ThreadId saved_owner = owner_;
430#endif // defined(DEBUG)
431
433 if (micros == kNoTimeout) {
434 // Wait forever.
435 int result = pthread_cond_wait(data_.cond(), data_.mutex());
436 VALIDATE_PTHREAD_RESULT(result);
437 } else {
438 struct timespec ts;
439 int64_t secs = micros / kMicrosecondsPerSecond;
440 if (secs > kMaxInt32) {
441 // Avoid truncation of overly large timeout values.
442 secs = kMaxInt32;
443 }
444 int64_t nanos =
446 ts.tv_sec = static_cast<int32_t>(secs);
447 ts.tv_nsec = static_cast<long>(nanos); // NOLINT (long used in timespec).
448 int result =
449 pthread_cond_timedwait_relative_np(data_.cond(), data_.mutex(), &ts);
450 ASSERT((result == 0) || (result == ETIMEDOUT));
451 if (result == ETIMEDOUT) {
452 retval = kTimedOut;
453 }
454 }
455
456#if defined(DEBUG)
457 // When running with assertions enabled we track the owner.
460 ASSERT(owner_ == saved_owner);
461#endif // defined(DEBUG)
462 return retval;
463}
464
465void Monitor::Notify() {
466 // When running with assertions enabled we track the owner.
468 int result = pthread_cond_signal(data_.cond());
469 VALIDATE_PTHREAD_RESULT(result);
470}
471
472void Monitor::NotifyAll() {
473 // When running with assertions enabled we track the owner.
475 int result = pthread_cond_broadcast(data_.cond());
476 VALIDATE_PTHREAD_RESULT(result);
477}
478
479} // namespace dart
480
481#endif // defined(DART_HOST_OS_MACOS)
#define NO_SANITIZE_ADDRESS
#define DEBUG_ASSERT(cond)
Definition: assert.h:321
bool IsOwnedByCurrentThread() const
Definition: os_thread.h:371
static constexpr int64_t kNoTimeout
Definition: os_thread.h:361
Mutex(NOT_IN_PRODUCT(const char *name="anonymous mutex"))
bool IsOwnedByCurrentThread() const
Definition: os_thread.h:402
static void DeleteThreadLocal(ThreadLocalKey key)
static int Start(const char *name, ThreadStartFunction function, uword parameter)
const char * name() const
Definition: os_thread.h:108
ThreadId id() const
Definition: os_thread.h:96
static bool GetCurrentStackBounds(uword *lower, uword *upper)
static OSThread * CreateOSThread()
Definition: os_thread.cc:66
static void SetCurrent(OSThread *current)
Definition: os_thread.h:186
static ThreadId ThreadIdFromIntPtr(intptr_t id)
static ThreadId GetCurrentThreadId()
static void Join(ThreadJoinId id)
static bool Compare(ThreadId a, ThreadId b)
static ThreadLocalKey CreateThreadLocal(ThreadDestructor destructor=nullptr)
static const ThreadId kInvalidThreadId
Definition: os_thread.h:248
static ThreadJoinId GetCurrentThreadJoinId(OSThread *thread)
void(* ThreadStartFunction)(uword parameter)
Definition: os_thread.h:209
static void SetThreadLocal(ThreadLocalKey key, uword value)
static intptr_t ThreadIdToIntPtr(ThreadId id)
static intptr_t GetMaxStackSize()
static const ThreadJoinId kInvalidThreadJoinId
Definition: os_thread.h:249
#define ASSERT(E)
static bool b
struct MyStruct a[10]
#define FATAL(error)
uint8_t value
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
Definition: dart_vm.cc:33
constexpr int kMinInt
Definition: globals.h:489
constexpr intptr_t kMicrosecondsPerMillisecond
Definition: globals.h:561
const char *const name
pthread_t ThreadJoinId
constexpr intptr_t kMicrosecondsPerSecond
Definition: globals.h:562
constexpr intptr_t kNanosecondsPerMicrosecond
Definition: globals.h:564
void * malloc(size_t size)
Definition: allocation.cc:19
constexpr intptr_t KB
Definition: globals.h:528
uintptr_t uword
Definition: globals.h:501
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
void(* ThreadDestructor)(void *parameter)
Definition: os_thread_win.h:69
constexpr int32_t kMaxInt32
Definition: globals.h:483
constexpr intptr_t kWordSize
Definition: globals.h:509
pthread_key_t ThreadLocalKey
static int8_t data[kExtLength]
NOT_IN_PRODUCT(LibraryPtr ReloadTestScript(const char *script))
static const ThreadLocalKey kUnsetThreadLocalKey
pthread_t ThreadId
COMPILE_ASSERT(kUnreachableReference==WeakTable::kNoValue)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network policy
Definition: switches.h:248
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: globals.h:581
#define NO_SANITIZE_SAFE_STACK
Definition: safe_stack.h:17
#define ARRAY_SIZE(array)
Definition: globals.h:72