1// Copyright (c) 2015, 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 "vm/os_thread.h"
6
7#include "platform/address_sanitizer.h"
8#include "platform/atomic.h"
9#include "vm/lockers.h"
10#include "vm/log.h"
11#include "vm/thread_interrupter.h"
12#include "vm/timeline.h"
13
14namespace dart {
15
16// The single thread local key which stores all the thread local data
17// for a thread.
18ThreadLocalKey OSThread::thread_key_ = kUnsetThreadLocalKey;
19OSThread* OSThread::thread_list_head_ = NULL;
20Mutex* OSThread::thread_list_lock_ = NULL;
21bool OSThread::creation_enabled_ = false;
22
23#if defined(HAS_C11_THREAD_LOCAL)
24thread_local ThreadState* OSThread::current_vm_thread_ = NULL;
25#endif
26
27OSThread::OSThread()
28 : BaseThread(true),
29 id_(OSThread::GetCurrentThreadId()),
30#if defined(DEBUG)
31 join_id_(kInvalidThreadJoinId),
32#endif
33#ifdef SUPPORT_TIMELINE
34 trace_id_(OSThread::GetCurrentThreadTraceId()),
35#endif
36 name_(NULL),
37 timeline_block_lock_(),
38 timeline_block_(NULL),
39 thread_list_next_(NULL),
40 thread_interrupt_disabled_(1), // Thread interrupts disabled by default.
41 log_(new class Log()),
42 stack_base_(0),
43 stack_limit_(0),
44 stack_headroom_(0),
45 thread_(NULL) {
46 // Try to get accurate stack bounds from pthreads, etc.
47 if (!GetCurrentStackBounds(&stack_limit_, &stack_base_)) {
48 FATAL("Failed to retrieve stack bounds");
49 }
50
51 stack_headroom_ = CalculateHeadroom(stack_base_ - stack_limit_);
52
53 ASSERT(stack_base_ != 0);
54 ASSERT(stack_limit_ != 0);
55 ASSERT(stack_base_ > stack_limit_);
56 ASSERT(stack_base_ > GetCurrentStackPointer());
57 ASSERT(stack_limit_ < GetCurrentStackPointer());
58 RELEASE_ASSERT(HasStackHeadroom());
59}
60
61OSThread* OSThread::CreateOSThread() {
62 ASSERT(thread_list_lock_ != NULL);
63 MutexLocker ml(thread_list_lock_);
64 if (!creation_enabled_) {
65 return NULL;
66 }
67 OSThread* os_thread = new OSThread();
68 AddThreadToListLocked(os_thread);
69 return os_thread;
70}
71
72OSThread::~OSThread() {
73 if (!is_os_thread()) {
74 // If the embedder enters an isolate on this thread and does not exit the
75 // isolate, the thread local at thread_key_, which we are destructing here,
76 // will contain a dart::Thread instead of a dart::OSThread.
77 FATAL("Thread exited without calling Dart_ExitIsolate");
78 }
79 RemoveThreadFromList(this);
80 delete log_;
81 log_ = NULL;
82#if defined(SUPPORT_TIMELINE)
83 if (Timeline::recorder() != NULL) {
84 Timeline::recorder()->FinishBlock(timeline_block_);
85 }
86#endif
87 timeline_block_ = NULL;
88 free(name_);
89}
90
91void OSThread::SetName(const char* name) {
92 MutexLocker ml(thread_list_lock_);
93 // Clear the old thread name.
94 if (name_ != NULL) {
95 free(name_);
96 name_ = NULL;
97 }
98 set_name(name);
99}
100
101// Disable AdressSanitizer and SafeStack transformation on this function. In
102// particular, taking the address of a local gives an address on the stack
103// instead of an address in the shadow memory (AddressSanitizer) or the safe
104// stack (SafeStack).
105NO_SANITIZE_ADDRESS
106NO_SANITIZE_SAFE_STACK
107DART_NOINLINE
108uword OSThread::GetCurrentStackPointer() {
109 uword stack_allocated_local = reinterpret_cast<uword>(&stack_allocated_local);
110 return stack_allocated_local;
111}
112
113void OSThread::DisableThreadInterrupts() {
114 ASSERT(OSThread::Current() == this);
115 thread_interrupt_disabled_.fetch_add(1u);
116}
117
118void OSThread::EnableThreadInterrupts() {
119 ASSERT(OSThread::Current() == this);
120 uintptr_t old = thread_interrupt_disabled_.fetch_sub(1u);
121 if (FLAG_profiler && (old == 1)) {
122 // We just decremented from 1 to 0.
123 // Make sure the thread interrupter is awake.
124 ThreadInterrupter::WakeUp();
125 }
126 if (old == 0) {
127 // We just decremented from 0, this means we've got a mismatched pair
128 // of calls to EnableThreadInterrupts and DisableThreadInterrupts.
129 FATAL("Invalid call to OSThread::EnableThreadInterrupts()");
130 }
131}
132
133bool OSThread::ThreadInterruptsEnabled() {
134 return thread_interrupt_disabled_ == 0;
135}
136
137static void DeleteThread(void* thread) {
138 delete reinterpret_cast<OSThread*>(thread);
139}
140
141void OSThread::Init() {
142 // Allocate the global OSThread lock.
143 if (thread_list_lock_ == NULL) {
144 thread_list_lock_ = new Mutex();
145 }
146 ASSERT(thread_list_lock_ != NULL);
147
148 // Create the thread local key.
149 if (thread_key_ == kUnsetThreadLocalKey) {
150 thread_key_ = CreateThreadLocal(DeleteThread);
151 }
152 ASSERT(thread_key_ != kUnsetThreadLocalKey);
153
154 // Enable creation of OSThread structures in the VM.
155 EnableOSThreadCreation();
156
157 // Create a new OSThread strcture and set it as the TLS.
158 OSThread* os_thread = CreateOSThread();
159 ASSERT(os_thread != NULL);
160 OSThread::SetCurrent(os_thread);
161 os_thread->set_name("Dart_Initialize");
162}
163
164void OSThread::Cleanup() {
165// We cannot delete the thread local key and thread list lock, yet.
166// See the note on thread_list_lock_ in os_thread.h.
167#if 0
168 if (thread_list_lock_ != NULL) {
169 // Delete the thread local key.
170 ASSERT(thread_key_ != kUnsetThreadLocalKey);
171 DeleteThreadLocal(thread_key_);
172 thread_key_ = kUnsetThreadLocalKey;
173
174 // Delete the global OSThread lock.
175 ASSERT(thread_list_lock_ != NULL);
176 delete thread_list_lock_;
177 thread_list_lock_ = NULL;
178 }
179#endif
180}
181
182OSThread* OSThread::CreateAndSetUnknownThread() {
183 ASSERT(OSThread::GetCurrentTLS() == NULL);
184 OSThread* os_thread = CreateOSThread();
185 if (os_thread != NULL) {
186 OSThread::SetCurrent(os_thread);
187 os_thread->set_name("Unknown");
188 }
189 return os_thread;
190}
191
192bool OSThread::IsThreadInList(ThreadId id) {
193 if (id == OSThread::kInvalidThreadId) {
194 return false;
195 }
196 OSThreadIterator it;
197 while (it.HasNext()) {
198 ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread());
199 OSThread* t = it.Next();
200 // An address test is not sufficient because the allocator may recycle
201 // the address for another Thread. Test against the thread's id.
202 if (t->id() == id) {
203 return true;
204 }
205 }
206 return false;
207}
208
209void OSThread::DisableOSThreadCreation() {
210 MutexLocker ml(thread_list_lock_);
211 creation_enabled_ = false;
212}
213
214void OSThread::EnableOSThreadCreation() {
215 MutexLocker ml(thread_list_lock_);
216 creation_enabled_ = true;
217}
218
219OSThread* OSThread::GetOSThreadFromThread(ThreadState* thread) {
220 ASSERT(thread->os_thread() != NULL);
221 return thread->os_thread();
222}
223
224void OSThread::AddThreadToListLocked(OSThread* thread) {
225 ASSERT(thread != NULL);
226 ASSERT(thread_list_lock_ != NULL);
227 ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread());
228 ASSERT(creation_enabled_);
229 ASSERT(thread->thread_list_next_ == NULL);
230
231#if defined(DEBUG)
232 {
233 // Ensure that we aren't already in the list.
234 OSThread* current = thread_list_head_;
235 while (current != NULL) {
236 ASSERT(current != thread);
237 current = current->thread_list_next_;
238 }
239 }
240#endif
241
242 // Insert at head of list.
243 thread->thread_list_next_ = thread_list_head_;
244 thread_list_head_ = thread;
245}
246
247void OSThread::RemoveThreadFromList(OSThread* thread) {
248 bool final_thread = false;
249 {
250 ASSERT(thread != NULL);
251 ASSERT(thread_list_lock_ != NULL);
252 MutexLocker ml(thread_list_lock_);
253 OSThread* current = thread_list_head_;
254 OSThread* previous = NULL;
255
256 // Scan across list and remove |thread|.
257 while (current != NULL) {
258 if (current == thread) {
259 // We found |thread|, remove from list.
260 if (previous == NULL) {
261 thread_list_head_ = thread->thread_list_next_;
262 } else {
263 previous->thread_list_next_ = current->thread_list_next_;
264 }
265 thread->thread_list_next_ = NULL;
266 final_thread = !creation_enabled_ && (thread_list_head_ == NULL);
267 break;
268 }
269 previous = current;
270 current = current->thread_list_next_;
271 }
272 }
273 // Check if this is the last thread. The last thread does a cleanup
274 // which removes the thread local key and the associated mutex.
275 if (final_thread) {
276 Cleanup();
277 }
278}
279
280void OSThread::SetCurrentTLS(BaseThread* value) {
281 // Provides thread-local destructors.
282 SetThreadLocal(thread_key_, reinterpret_cast<uword>(value));
283
284#if defined(HAS_C11_THREAD_LOCAL)
285 // Allows the C compiler more freedom to optimize.
286 if ((value != NULL) && !value->is_os_thread()) {
287 current_vm_thread_ = static_cast<Thread*>(value);
288 } else {
289 current_vm_thread_ = NULL;
290 }
291#endif
292}
293
294OSThreadIterator::OSThreadIterator() {
295 ASSERT(OSThread::thread_list_lock_ != NULL);
296 // Lock the thread list while iterating.
297 OSThread::thread_list_lock_->Lock();
298 next_ = OSThread::thread_list_head_;
299}
300
301OSThreadIterator::~OSThreadIterator() {
302 ASSERT(OSThread::thread_list_lock_ != NULL);
303 // Unlock the thread list when done.
304 OSThread::thread_list_lock_->Unlock();
305}
306
307bool OSThreadIterator::HasNext() const {
308 ASSERT(OSThread::thread_list_lock_ != NULL);
309 ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread());
310 return next_ != NULL;
311}
312
313OSThread* OSThreadIterator::Next() {
314 ASSERT(OSThread::thread_list_lock_ != NULL);
315 ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread());
316 OSThread* current = next_;
317 next_ = next_->thread_list_next_;
318 return current;
319}
320
321} // namespace dart
322