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_LINUX)
7
8#include "bin/thread.h"
9#include "bin/thread_linux.h"
10
11#include <errno.h> // NOLINT
12#include <sys/resource.h> // NOLINT
13#include <sys/time.h> // NOLINT
14
15#include "platform/assert.h"
16#include "platform/utils.h"
17
18namespace dart {
19namespace bin {
20
21#define VALIDATE_PTHREAD_RESULT(result) \
22 if (result != 0) { \
23 const int kBufferSize = 1024; \
24 char error_buf[kBufferSize]; \
25 FATAL2("pthread error: %d (%s)", result, \
26 Utils::StrError(result, error_buf, kBufferSize)); \
27 }
28
29#ifdef DEBUG
30#define RETURN_ON_PTHREAD_FAILURE(result) \
31 if (result != 0) { \
32 const int kBufferSize = 1024; \
33 char error_buf[kBufferSize]; \
34 fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \
35 result, Utils::StrError(result, error_buf, kBufferSize)); \
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 int64_t secs = micros / kMicrosecondsPerSecond;
47 int64_t nanos =
48 (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond;
49 int result = clock_gettime(CLOCK_MONOTONIC, ts);
50 ASSERT(result == 0);
51 ts->tv_sec += secs;
52 ts->tv_nsec += nanos;
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 pthread_self();
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 (pthread_equal(a, b) != 0);
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_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
237 VALIDATE_PTHREAD_RESULT(result);
238
239 result = pthread_cond_init(data_.cond(), &cond_attr);
240 VALIDATE_PTHREAD_RESULT(result);
241
242 result = pthread_condattr_destroy(&cond_attr);
243 VALIDATE_PTHREAD_RESULT(result);
244}
245
246Monitor::~Monitor() {
247 int result = pthread_mutex_destroy(data_.mutex());
248 VALIDATE_PTHREAD_RESULT(result);
249
250 result = pthread_cond_destroy(data_.cond());
251 VALIDATE_PTHREAD_RESULT(result);
252}
253
254void Monitor::Enter() {
255 int result = pthread_mutex_lock(data_.mutex());
256 VALIDATE_PTHREAD_RESULT(result);
257 // TODO(iposva): Do we need to track lock owners?
258}
259
260void Monitor::Exit() {
261 // TODO(iposva): Do we need to track lock owners?
262 int result = pthread_mutex_unlock(data_.mutex());
263 VALIDATE_PTHREAD_RESULT(result);
264}
265
266Monitor::WaitResult Monitor::Wait(int64_t millis) {
267 return WaitMicros(millis * kMicrosecondsPerMillisecond);
268}
269
270Monitor::WaitResult Monitor::WaitMicros(int64_t micros) {
271 // TODO(iposva): Do we need to track lock owners?
272 Monitor::WaitResult retval = kNotified;
273 if (micros == kNoTimeout) {
274 // Wait forever.
275 int result = pthread_cond_wait(data_.cond(), data_.mutex());
276 VALIDATE_PTHREAD_RESULT(result);
277 } else {
278 struct timespec ts;
279 ComputeTimeSpecMicros(&ts, micros);
280 int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts);
281 ASSERT((result == 0) || (result == ETIMEDOUT));
282 if (result == ETIMEDOUT) {
283 retval = kTimedOut;
284 }
285 }
286 return retval;
287}
288
289void Monitor::Notify() {
290 // TODO(iposva): Do we need to track lock owners?
291 int result = pthread_cond_signal(data_.cond());
292 VALIDATE_PTHREAD_RESULT(result);
293}
294
295void Monitor::NotifyAll() {
296 // TODO(iposva): Do we need to track lock owners?
297 int result = pthread_cond_broadcast(data_.cond());
298 VALIDATE_PTHREAD_RESULT(result);
299}
300
301} // namespace bin
302} // namespace dart
303
304#endif // defined(HOST_OS_LINUX)
305