blob: 80a7cd94fb8ce3b3e6e004b12c9f49e5bd001fcc [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "api.h"
31#include "bootstrapper.h"
32#include "debug.h"
33#include "execution.h"
34#include "v8threads.h"
35#include "regexp-stack.h"
36
37namespace v8 {
38
39static internal::Thread::LocalStorageKey thread_state_key =
40 internal::Thread::CreateThreadLocalKey();
41static internal::Thread::LocalStorageKey thread_id_key =
42 internal::Thread::CreateThreadLocalKey();
43
44
45// Track whether this V8 instance has ever called v8::Locker. This allows the
46// API code to verify that the lock is always held when V8 is being entered.
47bool Locker::active_ = false;
48
49
50// Constructor for the Locker object. Once the Locker is constructed the
51// current thread will be guaranteed to have the big V8 lock.
52Locker::Locker() : has_lock_(false), top_level_(true) {
53 // Record that the Locker has been used at least once.
54 active_ = true;
55 // Get the big lock if necessary.
56 if (!internal::ThreadManager::IsLockedByCurrentThread()) {
57 internal::ThreadManager::Lock();
58 has_lock_ = true;
59 // Make sure that V8 is initialized. Archiving of threads interferes
60 // with deserialization by adding additional root pointers, so we must
61 // initialize here, before anyone can call ~Locker() or Unlocker().
62 if (!internal::V8::IsRunning()) {
63 V8::Initialize();
64 }
65 // This may be a locker within an unlocker in which case we have to
66 // get the saved state for this thread and restore it.
67 if (internal::ThreadManager::RestoreThread()) {
68 top_level_ = false;
69 } else {
70 internal::ExecutionAccess access;
71 internal::StackGuard::ClearThread(access);
72 internal::StackGuard::InitThread(access);
73 }
74 }
75 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
76
77 // Make sure this thread is assigned a thread id.
78 internal::ThreadManager::AssignId();
79}
80
81
82bool Locker::IsLocked() {
83 return internal::ThreadManager::IsLockedByCurrentThread();
84}
85
86
87Locker::~Locker() {
88 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
89 if (has_lock_) {
90 if (top_level_) {
91 internal::ThreadManager::FreeThreadResources();
92 } else {
93 internal::ThreadManager::ArchiveThread();
94 }
95 internal::ThreadManager::Unlock();
96 }
97}
98
99
100Unlocker::Unlocker() {
101 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
102 internal::ThreadManager::ArchiveThread();
103 internal::ThreadManager::Unlock();
104}
105
106
107Unlocker::~Unlocker() {
108 ASSERT(!internal::ThreadManager::IsLockedByCurrentThread());
109 internal::ThreadManager::Lock();
110 internal::ThreadManager::RestoreThread();
111}
112
113
114void Locker::StartPreemption(int every_n_ms) {
115 v8::internal::ContextSwitcher::StartPreemption(every_n_ms);
116}
117
118
119void Locker::StopPreemption() {
120 v8::internal::ContextSwitcher::StopPreemption();
121}
122
123
124namespace internal {
125
126
127bool ThreadManager::RestoreThread() {
128 // First check whether the current thread has been 'lazily archived', ie
129 // not archived at all. If that is the case we put the state storage we
130 // had prepared back in the free list, since we didn't need it after all.
131 if (lazily_archived_thread_.IsSelf()) {
132 lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
133 ASSERT(Thread::GetThreadLocal(thread_state_key) ==
134 lazily_archived_thread_state_);
135 lazily_archived_thread_state_->set_id(kInvalidId);
136 lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
137 lazily_archived_thread_state_ = NULL;
138 Thread::SetThreadLocal(thread_state_key, NULL);
139 return true;
140 }
141
142 // Make sure that the preemption thread cannot modify the thread state while
143 // it is being archived or restored.
144 ExecutionAccess access;
145
146 // If there is another thread that was lazily archived then we have to really
147 // archive it now.
148 if (lazily_archived_thread_.IsValid()) {
149 EagerlyArchiveThread();
150 }
151 ThreadState* state =
152 reinterpret_cast<ThreadState*>(Thread::GetThreadLocal(thread_state_key));
153 if (state == NULL) {
154 // This is a new thread.
155 StackGuard::InitThread(access);
156 return false;
157 }
158 char* from = state->data();
159 from = HandleScopeImplementer::RestoreThread(from);
160 from = Top::RestoreThread(from);
161 from = Relocatable::RestoreState(from);
162#ifdef ENABLE_DEBUGGER_SUPPORT
163 from = Debug::RestoreDebug(from);
164#endif
165 from = StackGuard::RestoreStackGuard(from);
166 from = RegExpStack::RestoreStack(from);
167 from = Bootstrapper::RestoreState(from);
168 Thread::SetThreadLocal(thread_state_key, NULL);
169 if (state->terminate_on_restore()) {
170 StackGuard::TerminateExecution();
171 state->set_terminate_on_restore(false);
172 }
173 state->set_id(kInvalidId);
174 state->Unlink();
175 state->LinkInto(ThreadState::FREE_LIST);
176 return true;
177}
178
179
180void ThreadManager::Lock() {
181 mutex_->Lock();
182 mutex_owner_.Initialize(ThreadHandle::SELF);
183 ASSERT(IsLockedByCurrentThread());
184}
185
186
187void ThreadManager::Unlock() {
188 mutex_owner_.Initialize(ThreadHandle::INVALID);
189 mutex_->Unlock();
190}
191
192
193static int ArchiveSpacePerThread() {
194 return HandleScopeImplementer::ArchiveSpacePerThread() +
195 Top::ArchiveSpacePerThread() +
196#ifdef ENABLE_DEBUGGER_SUPPORT
197 Debug::ArchiveSpacePerThread() +
198#endif
199 StackGuard::ArchiveSpacePerThread() +
200 RegExpStack::ArchiveSpacePerThread() +
201 Bootstrapper::ArchiveSpacePerThread() +
202 Relocatable::ArchiveSpacePerThread();
203}
204
205
206ThreadState* ThreadState::free_anchor_ = new ThreadState();
207ThreadState* ThreadState::in_use_anchor_ = new ThreadState();
208
209
210ThreadState::ThreadState() : id_(ThreadManager::kInvalidId),
211 terminate_on_restore_(false),
212 next_(this), previous_(this) {
213}
214
215
216void ThreadState::AllocateSpace() {
217 data_ = NewArray<char>(ArchiveSpacePerThread());
218}
219
220
221void ThreadState::Unlink() {
222 next_->previous_ = previous_;
223 previous_->next_ = next_;
224}
225
226
227void ThreadState::LinkInto(List list) {
228 ThreadState* flying_anchor =
229 list == FREE_LIST ? free_anchor_ : in_use_anchor_;
230 next_ = flying_anchor->next_;
231 previous_ = flying_anchor;
232 flying_anchor->next_ = this;
233 next_->previous_ = this;
234}
235
236
237ThreadState* ThreadState::GetFree() {
238 ThreadState* gotten = free_anchor_->next_;
239 if (gotten == free_anchor_) {
240 ThreadState* new_thread_state = new ThreadState();
241 new_thread_state->AllocateSpace();
242 return new_thread_state;
243 }
244 return gotten;
245}
246
247
248// Gets the first in the list of archived threads.
249ThreadState* ThreadState::FirstInUse() {
250 return in_use_anchor_->Next();
251}
252
253
254ThreadState* ThreadState::Next() {
255 if (next_ == in_use_anchor_) return NULL;
256 return next_;
257}
258
259
260// Thread ids must start with 1, because in TLS having thread id 0 can't
261// be distinguished from not having a thread id at all (since NULL is
262// defined as 0.)
263int ThreadManager::last_id_ = 0;
264Mutex* ThreadManager::mutex_ = OS::CreateMutex();
265ThreadHandle ThreadManager::mutex_owner_(ThreadHandle::INVALID);
266ThreadHandle ThreadManager::lazily_archived_thread_(ThreadHandle::INVALID);
267ThreadState* ThreadManager::lazily_archived_thread_state_ = NULL;
268
269
270void ThreadManager::ArchiveThread() {
271 ASSERT(!lazily_archived_thread_.IsValid());
272 ASSERT(!IsArchived());
273 ThreadState* state = ThreadState::GetFree();
274 state->Unlink();
275 Thread::SetThreadLocal(thread_state_key, reinterpret_cast<void*>(state));
276 lazily_archived_thread_.Initialize(ThreadHandle::SELF);
277 lazily_archived_thread_state_ = state;
278 ASSERT(state->id() == kInvalidId);
279 state->set_id(CurrentId());
280 ASSERT(state->id() != kInvalidId);
281}
282
283
284void ThreadManager::EagerlyArchiveThread() {
285 ThreadState* state = lazily_archived_thread_state_;
286 state->LinkInto(ThreadState::IN_USE_LIST);
287 char* to = state->data();
288 // Ensure that data containing GC roots are archived first, and handle them
289 // in ThreadManager::Iterate(ObjectVisitor*).
290 to = HandleScopeImplementer::ArchiveThread(to);
291 to = Top::ArchiveThread(to);
292 to = Relocatable::ArchiveState(to);
293#ifdef ENABLE_DEBUGGER_SUPPORT
294 to = Debug::ArchiveDebug(to);
295#endif
296 to = StackGuard::ArchiveStackGuard(to);
297 to = RegExpStack::ArchiveStack(to);
298 to = Bootstrapper::ArchiveState(to);
299 lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
300 lazily_archived_thread_state_ = NULL;
301}
302
303
304void ThreadManager::FreeThreadResources() {
305 HandleScopeImplementer::FreeThreadResources();
306 Top::FreeThreadResources();
307#ifdef ENABLE_DEBUGGER_SUPPORT
308 Debug::FreeThreadResources();
309#endif
310 StackGuard::FreeThreadResources();
311 RegExpStack::FreeThreadResources();
312 Bootstrapper::FreeThreadResources();
313}
314
315
316bool ThreadManager::IsArchived() {
317 return Thread::HasThreadLocal(thread_state_key);
318}
319
320
321void ThreadManager::Iterate(ObjectVisitor* v) {
322 // Expecting no threads during serialization/deserialization
323 for (ThreadState* state = ThreadState::FirstInUse();
324 state != NULL;
325 state = state->Next()) {
326 char* data = state->data();
327 data = HandleScopeImplementer::Iterate(v, data);
328 data = Top::Iterate(v, data);
329 data = Relocatable::Iterate(v, data);
330 }
331}
332
333
334void ThreadManager::MarkCompactPrologue(bool is_compacting) {
335 for (ThreadState* state = ThreadState::FirstInUse();
336 state != NULL;
337 state = state->Next()) {
338 char* data = state->data();
339 data += HandleScopeImplementer::ArchiveSpacePerThread();
340 Top::MarkCompactPrologue(is_compacting, data);
341 }
342}
343
344
345void ThreadManager::MarkCompactEpilogue(bool is_compacting) {
346 for (ThreadState* state = ThreadState::FirstInUse();
347 state != NULL;
348 state = state->Next()) {
349 char* data = state->data();
350 data += HandleScopeImplementer::ArchiveSpacePerThread();
351 Top::MarkCompactEpilogue(is_compacting, data);
352 }
353}
354
355
356int ThreadManager::CurrentId() {
357 return Thread::GetThreadLocalInt(thread_id_key);
358}
359
360
361void ThreadManager::AssignId() {
362 if (!HasId()) {
363 ASSERT(Locker::IsLocked());
364 int thread_id = ++last_id_;
365 ASSERT(thread_id > 0); // see the comment near last_id_ definition.
366 Thread::SetThreadLocalInt(thread_id_key, thread_id);
367 Top::set_thread_id(thread_id);
368 }
369}
370
371
372bool ThreadManager::HasId() {
373 return Thread::HasThreadLocal(thread_id_key);
374}
375
376
377void ThreadManager::TerminateExecution(int thread_id) {
378 for (ThreadState* state = ThreadState::FirstInUse();
379 state != NULL;
380 state = state->Next()) {
381 if (thread_id == state->id()) {
382 state->set_terminate_on_restore(true);
383 }
384 }
385}
386
387
388// This is the ContextSwitcher singleton. There is at most a single thread
389// running which delivers preemption events to V8 threads.
390ContextSwitcher* ContextSwitcher::singleton_ = NULL;
391
392
393ContextSwitcher::ContextSwitcher(int every_n_ms)
394 : keep_going_(true),
395 sleep_ms_(every_n_ms) {
396}
397
398
399// Set the scheduling interval of V8 threads. This function starts the
400// ContextSwitcher thread if needed.
401void ContextSwitcher::StartPreemption(int every_n_ms) {
402 ASSERT(Locker::IsLocked());
403 if (singleton_ == NULL) {
404 // If the ContextSwitcher thread is not running at the moment start it now.
405 singleton_ = new ContextSwitcher(every_n_ms);
406 singleton_->Start();
407 } else {
408 // ContextSwitcher thread is already running, so we just change the
409 // scheduling interval.
410 singleton_->sleep_ms_ = every_n_ms;
411 }
412}
413
414
415// Disable preemption of V8 threads. If multiple threads want to use V8 they
416// must cooperatively schedule amongst them from this point on.
417void ContextSwitcher::StopPreemption() {
418 ASSERT(Locker::IsLocked());
419 if (singleton_ != NULL) {
420 // The ContextSwitcher thread is running. We need to stop it and release
421 // its resources.
422 singleton_->keep_going_ = false;
423 singleton_->Join(); // Wait for the ContextSwitcher thread to exit.
424 // Thread has exited, now we can delete it.
425 delete(singleton_);
426 singleton_ = NULL;
427 }
428}
429
430
431// Main loop of the ContextSwitcher thread: Preempt the currently running V8
432// thread at regular intervals.
433void ContextSwitcher::Run() {
434 while (keep_going_) {
435 OS::Sleep(sleep_ms_);
436 StackGuard::Preempt();
437 }
438}
439
440
441// Acknowledge the preemption by the receiving thread.
442void ContextSwitcher::PreemptionReceived() {
443 ASSERT(Locker::IsLocked());
444 // There is currently no accounting being done for this. But could be in the
445 // future, which is why we leave this in.
446}
447
448
449} // namespace internal
450} // namespace v8