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"
6#if defined(HOST_OS_MACOS)
7
8#include "bin/thread.h"
9#include "bin/thread_macos.h"
10
11#include <mach/mach_host.h> // NOLINT
12#include <mach/mach_init.h> // NOLINT
13#include <mach/mach_port.h> // NOLINT
14#include <mach/mach_traps.h> // NOLINT
15#include <mach/task_info.h> // NOLINT
16#include <mach/thread_act.h> // NOLINT
17#include <mach/thread_info.h> // NOLINT
18#include <sys/errno.h> // NOLINT
19#include <sys/sysctl.h> // NOLINT
20#include <sys/types.h> // NOLINT
21
22#include "platform/assert.h"
23#include "platform/utils.h"
24
25namespace dart {
26namespace bin {
27
28#define VALIDATE_PTHREAD_RESULT(result) \
29 if (result != 0) { \
30 const int kBufferSize = 1024; \
31 char error_message[kBufferSize]; \
32 Utils::StrError(result, error_message, kBufferSize); \
33 FATAL2("pthread error: %d (%s)", result, error_message); \
34 }
35
36#ifdef DEBUG
37#define RETURN_ON_PTHREAD_FAILURE(result) \
38 if (result != 0) { \
39 const int kBufferSize = 1024; \
40 char error_message[kBufferSize]; \
41 Utils::StrError(result, error_message, kBufferSize); \
42 fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
43 result, error_message); \
44 return result; \
45 }
46#else
47#define RETURN_ON_PTHREAD_FAILURE(result) \
48 if (result != 0) { \
49 return result; \
50 }
51#endif
52
53class ThreadStartData {
54 public:
55 ThreadStartData(const char* name,
56 Thread::ThreadStartFunction function,
57 uword parameter)
58 : name_(name), function_(function), parameter_(parameter) {}
59
60 const char* name() const { return name_; }
61 Thread::ThreadStartFunction function() const { return function_; }
62 uword parameter() const { return parameter_; }
63
64 private:
65 const char* name_;
66 Thread::ThreadStartFunction function_;
67 uword parameter_;
68
69 DISALLOW_COPY_AND_ASSIGN(ThreadStartData);
70};
71
72// Dispatch to the thread start function provided by the caller. This trampoline
73// is used to ensure that the thread is properly destroyed if the thread just
74// exits.
75static void* ThreadStart(void* data_ptr) {
76 ThreadStartData* data = reinterpret_cast<ThreadStartData*>(data_ptr);
77
78 const char* name = data->name();
79 Thread::ThreadStartFunction function = data->function();
80 uword parameter = data->parameter();
81 delete data;
82
83 // Set the thread name.
84 pthread_setname_np(name);
85
86 // Call the supplied thread start function handing it its parameters.
87 function(parameter);
88
89 return NULL;
90}
91
92int Thread::Start(const char* name,
93 ThreadStartFunction function,
94 uword parameter) {
95 pthread_attr_t attr;
96 int result = pthread_attr_init(&attr);
97 RETURN_ON_PTHREAD_FAILURE(result);
98
99 result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
100 RETURN_ON_PTHREAD_FAILURE(result);
101
102 result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize());
103 RETURN_ON_PTHREAD_FAILURE(result);
104
105 ThreadStartData* data = new ThreadStartData(name, function, parameter);
106
107 pthread_t tid;
108 result = pthread_create(&tid, &attr, ThreadStart, data);
109 RETURN_ON_PTHREAD_FAILURE(result);
110
111 result = pthread_attr_destroy(&attr);
112 RETURN_ON_PTHREAD_FAILURE(result);
113
114 return 0;
115}
116
117const ThreadLocalKey Thread::kUnsetThreadLocalKey =
118 static_cast<pthread_key_t>(-1);
119const ThreadId Thread::kInvalidThreadId = reinterpret_cast<ThreadId>(NULL);
120
121ThreadLocalKey Thread::CreateThreadLocal() {
122 pthread_key_t key = kUnsetThreadLocalKey;
123 int result = pthread_key_create(&key, NULL);
124 VALIDATE_PTHREAD_RESULT(result);
125 ASSERT(key != kUnsetThreadLocalKey);
126 return key;
127}
128
129void Thread::DeleteThreadLocal(ThreadLocalKey key) {
130 ASSERT(key != kUnsetThreadLocalKey);
131 int result = pthread_key_delete(key);
132 VALIDATE_PTHREAD_RESULT(result);
133}
134
135void Thread::SetThreadLocal(ThreadLocalKey key, uword value) {
136 ASSERT(key != kUnsetThreadLocalKey);
137 int result = pthread_setspecific(key, reinterpret_cast<void*>(value));
138 VALIDATE_PTHREAD_RESULT(result);
139}
140
141intptr_t Thread::GetMaxStackSize() {
142 const int kStackSize = (128 * kWordSize * KB);
143 return kStackSize;
144}
145
146ThreadId Thread::GetCurrentThreadId() {
147 return pthread_self();
148}
149
150intptr_t Thread::ThreadIdToIntPtr(ThreadId id) {
151 ASSERT(sizeof(id) == sizeof(intptr_t));
152 return reinterpret_cast<intptr_t>(id);
153}
154
155bool Thread::Compare(ThreadId a, ThreadId b) {
156 return (pthread_equal(a, b) != 0);
157}
158
159Mutex::Mutex() {
160 pthread_mutexattr_t attr;
161 int result = pthread_mutexattr_init(&attr);
162 VALIDATE_PTHREAD_RESULT(result);
163
164#if defined(DEBUG)
165 result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
166 VALIDATE_PTHREAD_RESULT(result);
167#endif // defined(DEBUG)
168
169 result = pthread_mutex_init(data_.mutex(), &attr);
170 // Verify that creating a pthread_mutex succeeded.
171 VALIDATE_PTHREAD_RESULT(result);
172
173 result = pthread_mutexattr_destroy(&attr);
174 VALIDATE_PTHREAD_RESULT(result);
175}
176
177Mutex::~Mutex() {
178 int result = pthread_mutex_destroy(data_.mutex());
179 // Verify that the pthread_mutex was destroyed.
180 VALIDATE_PTHREAD_RESULT(result);
181}
182
183void Mutex::Lock() {
184 int result = pthread_mutex_lock(data_.mutex());
185 // Specifically check for dead lock to help debugging.
186 ASSERT(result != EDEADLK);
187 ASSERT(result == 0); // Verify no other errors.
188 // TODO(iposva): Do we need to track lock owners?
189}
190
191bool Mutex::TryLock() {
192 int result = pthread_mutex_trylock(data_.mutex());
193 // Return false if the lock is busy and locking failed.
194 if ((result == EBUSY) || (result == EDEADLK)) {
195 return false;
196 }
197 ASSERT(result == 0); // Verify no other errors.
198 // TODO(iposva): Do we need to track lock owners?
199 return true;
200}
201
202void Mutex::Unlock() {
203 // TODO(iposva): Do we need to track lock owners?
204 int result = pthread_mutex_unlock(data_.mutex());
205 // Specifically check for wrong thread unlocking to aid debugging.
206 ASSERT(result != EPERM);
207 ASSERT(result == 0); // Verify no other errors.
208}
209
210Monitor::Monitor() {
211 pthread_mutexattr_t attr;
212 int result = pthread_mutexattr_init(&attr);
213 VALIDATE_PTHREAD_RESULT(result);
214
215#if defined(DEBUG)
216 result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
217 VALIDATE_PTHREAD_RESULT(result);
218#endif // defined(DEBUG)
219
220 result = pthread_mutex_init(data_.mutex(), &attr);
221 VALIDATE_PTHREAD_RESULT(result);
222
223 result = pthread_mutexattr_destroy(&attr);
224 VALIDATE_PTHREAD_RESULT(result);
225
226 result = pthread_cond_init(data_.cond(), NULL);
227 VALIDATE_PTHREAD_RESULT(result);
228}
229
230Monitor::~Monitor() {
231 int result = pthread_mutex_destroy(data_.mutex());
232 VALIDATE_PTHREAD_RESULT(result);
233
234 result = pthread_cond_destroy(data_.cond());
235 VALIDATE_PTHREAD_RESULT(result);
236}
237
238void Monitor::Enter() {
239 int result = pthread_mutex_lock(data_.mutex());
240 VALIDATE_PTHREAD_RESULT(result);
241 // TODO(iposva): Do we need to track lock owners?
242}
243
244void Monitor::Exit() {
245 // TODO(iposva): Do we need to track lock owners?
246 int result = pthread_mutex_unlock(data_.mutex());
247 VALIDATE_PTHREAD_RESULT(result);
248}
249
250Monitor::WaitResult Monitor::Wait(int64_t millis) {
251 return WaitMicros(millis * kMicrosecondsPerMillisecond);
252}
253
254Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
255 // TODO(iposva): Do we need to track lock owners?
256 Monitor::WaitResult retval = kNotified;
257 if (micros == kNoTimeout) {
258 // Wait forever.
259 int result = pthread_cond_wait(data_.cond(), data_.mutex());
260 VALIDATE_PTHREAD_RESULT(result);
261 } else {
262 struct timespec ts;
263 int64_t secs = micros / kMicrosecondsPerSecond;
264 if (secs > kMaxInt32) {
265 // Avoid truncation of overly large timeout values.
266 secs = kMaxInt32;
267 }
268 int64_t nanos =
269 (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond;
270 ts.tv_sec = static_cast<int32_t>(secs);
271 ts.tv_nsec = static_cast<long>(nanos); // NOLINT (long used in timespec).
272 int result =
273 pthread_cond_timedwait_relative_np(data_.cond(), data_.mutex(), &ts);
274 ASSERT((result == 0) || (result == ETIMEDOUT));
275 if (result == ETIMEDOUT) {
276 retval = kTimedOut;
277 }
278 }
279 return retval;
280}
281
282void Monitor::Notify() {
283 // TODO(iposva): Do we need to track lock owners?
284 int result = pthread_cond_signal(data_.cond());
285 VALIDATE_PTHREAD_RESULT(result);
286}
287
288void Monitor::NotifyAll() {
289 // TODO(iposva): Do we need to track lock owners?
290 int result = pthread_cond_broadcast(data_.cond());
291 VALIDATE_PTHREAD_RESULT(result);
292}
293
294} // namespace bin
295} // namespace dart
296
297#endif // defined(HOST_OS_MACOS)
298