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