blob: 8a5fe6902dbbdcd76dd0eb6bd73005cca630efb6 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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"
ager@chromium.orgddb913d2009-01-27 10:01:48 +000031#include "bootstrapper.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "debug.h"
33#include "execution.h"
34#include "v8threads.h"
ager@chromium.org32912102009-01-16 10:38:43 +000035#include "regexp-stack.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036
37namespace v8 {
38
39static internal::Thread::LocalStorageKey thread_state_key =
40 internal::Thread::CreateThreadLocalKey();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000041static internal::Thread::LocalStorageKey thread_id_key =
42 internal::Thread::CreateThreadLocalKey();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043
ager@chromium.orgddb913d2009-01-27 10:01:48 +000044
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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050// 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) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +000053 // Record that the Locker has been used at least once.
54 active_ = true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055 // Get the big lock if necessary.
56 if (!internal::ThreadManager::IsLockedByCurrentThread()) {
57 internal::ThreadManager::Lock();
58 has_lock_ = true;
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +000059 // 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 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065 // 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;
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +000069 } else {
70 internal::ExecutionAccess access;
71 internal::StackGuard::ClearThread(access);
72 internal::StackGuard::InitThread(access);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000073 }
74 }
75 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076
77 // Make sure this thread is assigned a thread id.
78 internal::ThreadManager::AssignId();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000079}
80
81
ager@chromium.org9258b6b2008-09-11 09:11:10 +000082bool Locker::IsLocked() {
83 return internal::ThreadManager::IsLockedByCurrentThread();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000085
86
87Locker::~Locker() {
88 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
89 if (has_lock_) {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +000090 if (top_level_) {
91 internal::ThreadManager::FreeThreadResources();
92 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000093 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_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000135 lazily_archived_thread_state_->set_id(kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000136 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 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000141
142 // Make sure that the preemption thread cannot modify the thread state while
143 // it is being archived or restored.
144 ExecutionAccess access;
145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000146 // 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) {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000154 // This is a new thread.
155 StackGuard::InitThread(access);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156 return false;
157 }
158 char* from = state->data();
159 from = HandleScopeImplementer::RestoreThread(from);
160 from = Top::RestoreThread(from);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000161 from = Relocatable::RestoreState(from);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000162#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000163 from = Debug::RestoreDebug(from);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000164#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000165 from = StackGuard::RestoreStackGuard(from);
ager@chromium.org32912102009-01-16 10:38:43 +0000166 from = RegExpStack::RestoreStack(from);
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000167 from = Bootstrapper::RestoreState(from);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000168 Thread::SetThreadLocal(thread_state_key, NULL);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000169 if (state->terminate_on_restore()) {
170 StackGuard::TerminateExecution();
171 state->set_terminate_on_restore(false);
172 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 state->set_id(kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000174 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() +
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000196#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000197 Debug::ArchiveSpacePerThread() +
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000198#endif
ager@chromium.org32912102009-01-16 10:38:43 +0000199 StackGuard::ArchiveSpacePerThread() +
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000200 RegExpStack::ArchiveSpacePerThread() +
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000201 Bootstrapper::ArchiveSpacePerThread() +
202 Relocatable::ArchiveSpacePerThread();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203}
204
205
206ThreadState* ThreadState::free_anchor_ = new ThreadState();
207ThreadState* ThreadState::in_use_anchor_ = new ThreadState();
208
209
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000210ThreadState::ThreadState() : id_(ThreadManager::kInvalidId),
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000211 terminate_on_restore_(false),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000212 next_(this), previous_(this) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000213}
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
ager@chromium.orga1645e22009-09-09 19:27:10 +0000260// 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;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264Mutex* 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());
ager@chromium.orga1645e22009-09-09 19:27:10 +0000272 ASSERT(!IsArchived());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 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;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000278 ASSERT(state->id() == kInvalidId);
279 state->set_id(CurrentId());
280 ASSERT(state->id() != kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000281}
282
283
284void ThreadManager::EagerlyArchiveThread() {
285 ThreadState* state = lazily_archived_thread_state_;
286 state->LinkInto(ThreadState::IN_USE_LIST);
287 char* to = state->data();
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000288 // Ensure that data containing GC roots are archived first, and handle them
289 // in ThreadManager::Iterate(ObjectVisitor*).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 to = HandleScopeImplementer::ArchiveThread(to);
291 to = Top::ArchiveThread(to);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000292 to = Relocatable::ArchiveState(to);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000293#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 to = Debug::ArchiveDebug(to);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000295#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 to = StackGuard::ArchiveStackGuard(to);
ager@chromium.org32912102009-01-16 10:38:43 +0000297 to = RegExpStack::ArchiveStack(to);
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000298 to = Bootstrapper::ArchiveState(to);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299 lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
300 lazily_archived_thread_state_ = NULL;
301}
302
303
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000304void 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
ager@chromium.orga1645e22009-09-09 19:27:10 +0000316bool ThreadManager::IsArchived() {
317 return Thread::HasThreadLocal(thread_state_key);
318}
319
320
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000321void 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);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000329 data = Relocatable::Iterate(v, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 }
331}
332
333
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000334void ThreadManager::IterateArchivedThreads(ThreadVisitor* v) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000335 for (ThreadState* state = ThreadState::FirstInUse();
336 state != NULL;
337 state = state->Next()) {
338 char* data = state->data();
339 data += HandleScopeImplementer::ArchiveSpacePerThread();
340 Top::IterateThread(v, data);
341 }
342}
343
344
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000345int ThreadManager::CurrentId() {
ager@chromium.org9085a012009-05-11 19:22:57 +0000346 return Thread::GetThreadLocalInt(thread_id_key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000347}
348
349
350void ThreadManager::AssignId() {
ager@chromium.orga1645e22009-09-09 19:27:10 +0000351 if (!HasId()) {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000352 ASSERT(Locker::IsLocked());
ager@chromium.orga1645e22009-09-09 19:27:10 +0000353 int thread_id = ++last_id_;
354 ASSERT(thread_id > 0); // see the comment near last_id_ definition.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000355 Thread::SetThreadLocalInt(thread_id_key, thread_id);
356 Top::set_thread_id(thread_id);
357 }
358}
359
360
ager@chromium.orga1645e22009-09-09 19:27:10 +0000361bool ThreadManager::HasId() {
362 return Thread::HasThreadLocal(thread_id_key);
363}
364
365
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000366void ThreadManager::TerminateExecution(int thread_id) {
367 for (ThreadState* state = ThreadState::FirstInUse();
368 state != NULL;
369 state = state->Next()) {
370 if (thread_id == state->id()) {
371 state->set_terminate_on_restore(true);
372 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000373 }
374}
375
376
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000377// This is the ContextSwitcher singleton. There is at most a single thread
378// running which delivers preemption events to V8 threads.
379ContextSwitcher* ContextSwitcher::singleton_ = NULL;
380
381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000382ContextSwitcher::ContextSwitcher(int every_n_ms)
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000383 : Thread("v8:CtxtSwitcher"),
384 keep_going_(true),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000385 sleep_ms_(every_n_ms) {
386}
387
388
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000389// Set the scheduling interval of V8 threads. This function starts the
390// ContextSwitcher thread if needed.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391void ContextSwitcher::StartPreemption(int every_n_ms) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000392 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000393 if (singleton_ == NULL) {
394 // If the ContextSwitcher thread is not running at the moment start it now.
395 singleton_ = new ContextSwitcher(every_n_ms);
396 singleton_->Start();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000397 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000398 // ContextSwitcher thread is already running, so we just change the
399 // scheduling interval.
400 singleton_->sleep_ms_ = every_n_ms;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000401 }
402}
403
404
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000405// Disable preemption of V8 threads. If multiple threads want to use V8 they
406// must cooperatively schedule amongst them from this point on.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407void ContextSwitcher::StopPreemption() {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000408 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000409 if (singleton_ != NULL) {
410 // The ContextSwitcher thread is running. We need to stop it and release
411 // its resources.
412 singleton_->keep_going_ = false;
413 singleton_->Join(); // Wait for the ContextSwitcher thread to exit.
414 // Thread has exited, now we can delete it.
415 delete(singleton_);
416 singleton_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000417 }
418}
419
420
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000421// Main loop of the ContextSwitcher thread: Preempt the currently running V8
422// thread at regular intervals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000423void ContextSwitcher::Run() {
424 while (keep_going_) {
425 OS::Sleep(sleep_ms_);
426 StackGuard::Preempt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000427 }
428}
429
430
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000431// Acknowledge the preemption by the receiving thread.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000432void ContextSwitcher::PreemptionReceived() {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000433 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000434 // There is currently no accounting being done for this. But could be in the
435 // future, which is why we leave this in.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000436}
437
438
439} // namespace internal
440} // namespace v8