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