1// 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 451