blob: 243947636012cf4030782e2b9de472e3d824fe97 [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;
59 // This may be a locker within an unlocker in which case we have to
60 // get the saved state for this thread and restore it.
61 if (internal::ThreadManager::RestoreThread()) {
62 top_level_ = false;
63 }
64 }
65 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000066
67 // Make sure this thread is assigned a thread id.
68 internal::ThreadManager::AssignId();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000069}
70
71
ager@chromium.org9258b6b2008-09-11 09:11:10 +000072bool Locker::IsLocked() {
73 return internal::ThreadManager::IsLockedByCurrentThread();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000074}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000075
76
77Locker::~Locker() {
78 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
79 if (has_lock_) {
80 if (!top_level_) {
81 internal::ThreadManager::ArchiveThread();
82 }
83 internal::ThreadManager::Unlock();
84 }
85}
86
87
88Unlocker::Unlocker() {
89 ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
90 internal::ThreadManager::ArchiveThread();
91 internal::ThreadManager::Unlock();
92}
93
94
95Unlocker::~Unlocker() {
96 ASSERT(!internal::ThreadManager::IsLockedByCurrentThread());
97 internal::ThreadManager::Lock();
98 internal::ThreadManager::RestoreThread();
99}
100
101
102void Locker::StartPreemption(int every_n_ms) {
103 v8::internal::ContextSwitcher::StartPreemption(every_n_ms);
104}
105
106
107void Locker::StopPreemption() {
108 v8::internal::ContextSwitcher::StopPreemption();
109}
110
111
112namespace internal {
113
114
115bool ThreadManager::RestoreThread() {
116 // First check whether the current thread has been 'lazily archived', ie
117 // not archived at all. If that is the case we put the state storage we
118 // had prepared back in the free list, since we didn't need it after all.
119 if (lazily_archived_thread_.IsSelf()) {
120 lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
121 ASSERT(Thread::GetThreadLocal(thread_state_key) ==
122 lazily_archived_thread_state_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 lazily_archived_thread_state_->set_id(kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000124 lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
125 lazily_archived_thread_state_ = NULL;
126 Thread::SetThreadLocal(thread_state_key, NULL);
127 return true;
128 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000129
130 // Make sure that the preemption thread cannot modify the thread state while
131 // it is being archived or restored.
132 ExecutionAccess access;
133
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000134 // If there is another thread that was lazily archived then we have to really
135 // archive it now.
136 if (lazily_archived_thread_.IsValid()) {
137 EagerlyArchiveThread();
138 }
139 ThreadState* state =
140 reinterpret_cast<ThreadState*>(Thread::GetThreadLocal(thread_state_key));
141 if (state == NULL) {
142 return false;
143 }
144 char* from = state->data();
145 from = HandleScopeImplementer::RestoreThread(from);
146 from = Top::RestoreThread(from);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000147#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000148 from = Debug::RestoreDebug(from);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000149#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000150 from = StackGuard::RestoreStackGuard(from);
ager@chromium.org32912102009-01-16 10:38:43 +0000151 from = RegExpStack::RestoreStack(from);
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000152 from = Bootstrapper::RestoreState(from);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153 Thread::SetThreadLocal(thread_state_key, NULL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000154 state->set_id(kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000155 state->Unlink();
156 state->LinkInto(ThreadState::FREE_LIST);
157 return true;
158}
159
160
161void ThreadManager::Lock() {
162 mutex_->Lock();
163 mutex_owner_.Initialize(ThreadHandle::SELF);
164 ASSERT(IsLockedByCurrentThread());
165}
166
167
168void ThreadManager::Unlock() {
169 mutex_owner_.Initialize(ThreadHandle::INVALID);
170 mutex_->Unlock();
171}
172
173
174static int ArchiveSpacePerThread() {
175 return HandleScopeImplementer::ArchiveSpacePerThread() +
176 Top::ArchiveSpacePerThread() +
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000177#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000178 Debug::ArchiveSpacePerThread() +
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000179#endif
ager@chromium.org32912102009-01-16 10:38:43 +0000180 StackGuard::ArchiveSpacePerThread() +
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000181 RegExpStack::ArchiveSpacePerThread() +
182 Bootstrapper::ArchiveSpacePerThread();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000183}
184
185
186ThreadState* ThreadState::free_anchor_ = new ThreadState();
187ThreadState* ThreadState::in_use_anchor_ = new ThreadState();
188
189
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000190ThreadState::ThreadState() : id_(ThreadManager::kInvalidId),
191 next_(this), previous_(this) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000192}
193
194
195void ThreadState::AllocateSpace() {
196 data_ = NewArray<char>(ArchiveSpacePerThread());
197}
198
199
200void ThreadState::Unlink() {
201 next_->previous_ = previous_;
202 previous_->next_ = next_;
203}
204
205
206void ThreadState::LinkInto(List list) {
207 ThreadState* flying_anchor =
208 list == FREE_LIST ? free_anchor_ : in_use_anchor_;
209 next_ = flying_anchor->next_;
210 previous_ = flying_anchor;
211 flying_anchor->next_ = this;
212 next_->previous_ = this;
213}
214
215
216ThreadState* ThreadState::GetFree() {
217 ThreadState* gotten = free_anchor_->next_;
218 if (gotten == free_anchor_) {
219 ThreadState* new_thread_state = new ThreadState();
220 new_thread_state->AllocateSpace();
221 return new_thread_state;
222 }
223 return gotten;
224}
225
226
227// Gets the first in the list of archived threads.
228ThreadState* ThreadState::FirstInUse() {
229 return in_use_anchor_->Next();
230}
231
232
233ThreadState* ThreadState::Next() {
234 if (next_ == in_use_anchor_) return NULL;
235 return next_;
236}
237
238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000239int ThreadManager::next_id_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000240Mutex* ThreadManager::mutex_ = OS::CreateMutex();
241ThreadHandle ThreadManager::mutex_owner_(ThreadHandle::INVALID);
242ThreadHandle ThreadManager::lazily_archived_thread_(ThreadHandle::INVALID);
243ThreadState* ThreadManager::lazily_archived_thread_state_ = NULL;
244
245
246void ThreadManager::ArchiveThread() {
247 ASSERT(!lazily_archived_thread_.IsValid());
248 ASSERT(Thread::GetThreadLocal(thread_state_key) == NULL);
249 ThreadState* state = ThreadState::GetFree();
250 state->Unlink();
251 Thread::SetThreadLocal(thread_state_key, reinterpret_cast<void*>(state));
252 lazily_archived_thread_.Initialize(ThreadHandle::SELF);
253 lazily_archived_thread_state_ = state;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000254 ASSERT(state->id() == kInvalidId);
255 state->set_id(CurrentId());
256 ASSERT(state->id() != kInvalidId);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000257}
258
259
260void ThreadManager::EagerlyArchiveThread() {
261 ThreadState* state = lazily_archived_thread_state_;
262 state->LinkInto(ThreadState::IN_USE_LIST);
263 char* to = state->data();
264 to = HandleScopeImplementer::ArchiveThread(to);
265 to = Top::ArchiveThread(to);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000266#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000267 to = Debug::ArchiveDebug(to);
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000268#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000269 to = StackGuard::ArchiveStackGuard(to);
ager@chromium.org32912102009-01-16 10:38:43 +0000270 to = RegExpStack::ArchiveStack(to);
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000271 to = Bootstrapper::ArchiveState(to);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000272 lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
273 lazily_archived_thread_state_ = NULL;
274}
275
276
277void ThreadManager::Iterate(ObjectVisitor* v) {
278 // Expecting no threads during serialization/deserialization
279 for (ThreadState* state = ThreadState::FirstInUse();
280 state != NULL;
281 state = state->Next()) {
282 char* data = state->data();
283 data = HandleScopeImplementer::Iterate(v, data);
284 data = Top::Iterate(v, data);
285 }
286}
287
288
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000289void ThreadManager::MarkCompactPrologue(bool is_compacting) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 for (ThreadState* state = ThreadState::FirstInUse();
291 state != NULL;
292 state = state->Next()) {
293 char* data = state->data();
294 data += HandleScopeImplementer::ArchiveSpacePerThread();
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000295 Top::MarkCompactPrologue(is_compacting, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 }
297}
298
299
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000300void ThreadManager::MarkCompactEpilogue(bool is_compacting) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 for (ThreadState* state = ThreadState::FirstInUse();
302 state != NULL;
303 state = state->Next()) {
304 char* data = state->data();
305 data += HandleScopeImplementer::ArchiveSpacePerThread();
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000306 Top::MarkCompactEpilogue(is_compacting, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000307 }
308}
309
310
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311int ThreadManager::CurrentId() {
312 return bit_cast<int, void*>(Thread::GetThreadLocal(thread_id_key));
313}
314
315
316void ThreadManager::AssignId() {
317 if (Thread::GetThreadLocal(thread_id_key) == NULL) {
318 Thread::SetThreadLocal(thread_id_key, bit_cast<void*, int>(next_id_++));
319 }
320}
321
322
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000323// This is the ContextSwitcher singleton. There is at most a single thread
324// running which delivers preemption events to V8 threads.
325ContextSwitcher* ContextSwitcher::singleton_ = NULL;
326
327
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000328ContextSwitcher::ContextSwitcher(int every_n_ms)
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000329 : keep_going_(true),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 sleep_ms_(every_n_ms) {
331}
332
333
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000334// Set the scheduling interval of V8 threads. This function starts the
335// ContextSwitcher thread if needed.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336void ContextSwitcher::StartPreemption(int every_n_ms) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000337 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000338 if (singleton_ == NULL) {
339 // If the ContextSwitcher thread is not running at the moment start it now.
340 singleton_ = new ContextSwitcher(every_n_ms);
341 singleton_->Start();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000342 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000343 // ContextSwitcher thread is already running, so we just change the
344 // scheduling interval.
345 singleton_->sleep_ms_ = every_n_ms;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346 }
347}
348
349
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000350// Disable preemption of V8 threads. If multiple threads want to use V8 they
351// must cooperatively schedule amongst them from this point on.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000352void ContextSwitcher::StopPreemption() {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000353 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000354 if (singleton_ != NULL) {
355 // The ContextSwitcher thread is running. We need to stop it and release
356 // its resources.
357 singleton_->keep_going_ = false;
358 singleton_->Join(); // Wait for the ContextSwitcher thread to exit.
359 // Thread has exited, now we can delete it.
360 delete(singleton_);
361 singleton_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000362 }
363}
364
365
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000366// Main loop of the ContextSwitcher thread: Preempt the currently running V8
367// thread at regular intervals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368void ContextSwitcher::Run() {
369 while (keep_going_) {
370 OS::Sleep(sleep_ms_);
371 StackGuard::Preempt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 }
373}
374
375
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000376// Acknowledge the preemption by the receiving thread.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377void ContextSwitcher::PreemptionReceived() {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000378 ASSERT(Locker::IsLocked());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000379 // There is currently no accounting being done for this. But could be in the
380 // future, which is why we leave this in.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000381}
382
383
384} // namespace internal
385} // namespace v8