ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 1 | // Copyright 2008 the V8 project authors. All rights reserved. |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 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 "debug.h" |
| 32 | #include "execution.h" |
| 33 | #include "v8threads.h" |
| 34 | |
| 35 | namespace v8 { |
| 36 | |
| 37 | static internal::Thread::LocalStorageKey thread_state_key = |
| 38 | internal::Thread::CreateThreadLocalKey(); |
| 39 | |
| 40 | // Constructor for the Locker object. Once the Locker is constructed the |
| 41 | // current thread will be guaranteed to have the big V8 lock. |
| 42 | Locker::Locker() : has_lock_(false), top_level_(true) { |
| 43 | // Get the big lock if necessary. |
| 44 | if (!internal::ThreadManager::IsLockedByCurrentThread()) { |
| 45 | internal::ThreadManager::Lock(); |
| 46 | has_lock_ = true; |
| 47 | // This may be a locker within an unlocker in which case we have to |
| 48 | // get the saved state for this thread and restore it. |
| 49 | if (internal::ThreadManager::RestoreThread()) { |
| 50 | top_level_ = false; |
| 51 | } |
| 52 | } |
| 53 | ASSERT(internal::ThreadManager::IsLockedByCurrentThread()); |
| 54 | } |
| 55 | |
| 56 | |
ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 57 | bool Locker::IsLocked() { |
| 58 | return internal::ThreadManager::IsLockedByCurrentThread(); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 59 | } |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 60 | |
| 61 | |
| 62 | Locker::~Locker() { |
| 63 | ASSERT(internal::ThreadManager::IsLockedByCurrentThread()); |
| 64 | if (has_lock_) { |
| 65 | if (!top_level_) { |
| 66 | internal::ThreadManager::ArchiveThread(); |
| 67 | } |
| 68 | internal::ThreadManager::Unlock(); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | |
| 73 | Unlocker::Unlocker() { |
| 74 | ASSERT(internal::ThreadManager::IsLockedByCurrentThread()); |
| 75 | internal::ThreadManager::ArchiveThread(); |
| 76 | internal::ThreadManager::Unlock(); |
| 77 | } |
| 78 | |
| 79 | |
| 80 | Unlocker::~Unlocker() { |
| 81 | ASSERT(!internal::ThreadManager::IsLockedByCurrentThread()); |
| 82 | internal::ThreadManager::Lock(); |
| 83 | internal::ThreadManager::RestoreThread(); |
| 84 | } |
| 85 | |
| 86 | |
| 87 | void Locker::StartPreemption(int every_n_ms) { |
| 88 | v8::internal::ContextSwitcher::StartPreemption(every_n_ms); |
| 89 | } |
| 90 | |
| 91 | |
| 92 | void Locker::StopPreemption() { |
| 93 | v8::internal::ContextSwitcher::StopPreemption(); |
| 94 | } |
| 95 | |
| 96 | |
| 97 | namespace internal { |
| 98 | |
| 99 | |
| 100 | bool ThreadManager::RestoreThread() { |
| 101 | // First check whether the current thread has been 'lazily archived', ie |
| 102 | // not archived at all. If that is the case we put the state storage we |
| 103 | // had prepared back in the free list, since we didn't need it after all. |
| 104 | if (lazily_archived_thread_.IsSelf()) { |
| 105 | lazily_archived_thread_.Initialize(ThreadHandle::INVALID); |
| 106 | ASSERT(Thread::GetThreadLocal(thread_state_key) == |
| 107 | lazily_archived_thread_state_); |
| 108 | lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST); |
| 109 | lazily_archived_thread_state_ = NULL; |
| 110 | Thread::SetThreadLocal(thread_state_key, NULL); |
| 111 | return true; |
| 112 | } |
| 113 | // If there is another thread that was lazily archived then we have to really |
| 114 | // archive it now. |
| 115 | if (lazily_archived_thread_.IsValid()) { |
| 116 | EagerlyArchiveThread(); |
| 117 | } |
| 118 | ThreadState* state = |
| 119 | reinterpret_cast<ThreadState*>(Thread::GetThreadLocal(thread_state_key)); |
| 120 | if (state == NULL) { |
| 121 | return false; |
| 122 | } |
| 123 | char* from = state->data(); |
| 124 | from = HandleScopeImplementer::RestoreThread(from); |
| 125 | from = Top::RestoreThread(from); |
| 126 | from = Debug::RestoreDebug(from); |
| 127 | from = StackGuard::RestoreStackGuard(from); |
| 128 | Thread::SetThreadLocal(thread_state_key, NULL); |
| 129 | state->Unlink(); |
| 130 | state->LinkInto(ThreadState::FREE_LIST); |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | |
| 135 | void ThreadManager::Lock() { |
| 136 | mutex_->Lock(); |
| 137 | mutex_owner_.Initialize(ThreadHandle::SELF); |
| 138 | ASSERT(IsLockedByCurrentThread()); |
| 139 | } |
| 140 | |
| 141 | |
| 142 | void ThreadManager::Unlock() { |
| 143 | mutex_owner_.Initialize(ThreadHandle::INVALID); |
| 144 | mutex_->Unlock(); |
| 145 | } |
| 146 | |
| 147 | |
| 148 | static int ArchiveSpacePerThread() { |
| 149 | return HandleScopeImplementer::ArchiveSpacePerThread() + |
| 150 | Top::ArchiveSpacePerThread() + |
| 151 | Debug::ArchiveSpacePerThread() + |
| 152 | StackGuard::ArchiveSpacePerThread(); |
| 153 | } |
| 154 | |
| 155 | |
| 156 | ThreadState* ThreadState::free_anchor_ = new ThreadState(); |
| 157 | ThreadState* ThreadState::in_use_anchor_ = new ThreadState(); |
| 158 | |
| 159 | |
| 160 | ThreadState::ThreadState() : next_(this), previous_(this) { |
| 161 | } |
| 162 | |
| 163 | |
| 164 | void ThreadState::AllocateSpace() { |
| 165 | data_ = NewArray<char>(ArchiveSpacePerThread()); |
| 166 | } |
| 167 | |
| 168 | |
| 169 | void ThreadState::Unlink() { |
| 170 | next_->previous_ = previous_; |
| 171 | previous_->next_ = next_; |
| 172 | } |
| 173 | |
| 174 | |
| 175 | void ThreadState::LinkInto(List list) { |
| 176 | ThreadState* flying_anchor = |
| 177 | list == FREE_LIST ? free_anchor_ : in_use_anchor_; |
| 178 | next_ = flying_anchor->next_; |
| 179 | previous_ = flying_anchor; |
| 180 | flying_anchor->next_ = this; |
| 181 | next_->previous_ = this; |
| 182 | } |
| 183 | |
| 184 | |
| 185 | ThreadState* ThreadState::GetFree() { |
| 186 | ThreadState* gotten = free_anchor_->next_; |
| 187 | if (gotten == free_anchor_) { |
| 188 | ThreadState* new_thread_state = new ThreadState(); |
| 189 | new_thread_state->AllocateSpace(); |
| 190 | return new_thread_state; |
| 191 | } |
| 192 | return gotten; |
| 193 | } |
| 194 | |
| 195 | |
| 196 | // Gets the first in the list of archived threads. |
| 197 | ThreadState* ThreadState::FirstInUse() { |
| 198 | return in_use_anchor_->Next(); |
| 199 | } |
| 200 | |
| 201 | |
| 202 | ThreadState* ThreadState::Next() { |
| 203 | if (next_ == in_use_anchor_) return NULL; |
| 204 | return next_; |
| 205 | } |
| 206 | |
| 207 | |
| 208 | Mutex* ThreadManager::mutex_ = OS::CreateMutex(); |
| 209 | ThreadHandle ThreadManager::mutex_owner_(ThreadHandle::INVALID); |
| 210 | ThreadHandle ThreadManager::lazily_archived_thread_(ThreadHandle::INVALID); |
| 211 | ThreadState* ThreadManager::lazily_archived_thread_state_ = NULL; |
| 212 | |
| 213 | |
| 214 | void ThreadManager::ArchiveThread() { |
| 215 | ASSERT(!lazily_archived_thread_.IsValid()); |
| 216 | ASSERT(Thread::GetThreadLocal(thread_state_key) == NULL); |
| 217 | ThreadState* state = ThreadState::GetFree(); |
| 218 | state->Unlink(); |
| 219 | Thread::SetThreadLocal(thread_state_key, reinterpret_cast<void*>(state)); |
| 220 | lazily_archived_thread_.Initialize(ThreadHandle::SELF); |
| 221 | lazily_archived_thread_state_ = state; |
| 222 | } |
| 223 | |
| 224 | |
| 225 | void ThreadManager::EagerlyArchiveThread() { |
| 226 | ThreadState* state = lazily_archived_thread_state_; |
| 227 | state->LinkInto(ThreadState::IN_USE_LIST); |
| 228 | char* to = state->data(); |
| 229 | to = HandleScopeImplementer::ArchiveThread(to); |
| 230 | to = Top::ArchiveThread(to); |
| 231 | to = Debug::ArchiveDebug(to); |
| 232 | to = StackGuard::ArchiveStackGuard(to); |
| 233 | lazily_archived_thread_.Initialize(ThreadHandle::INVALID); |
| 234 | lazily_archived_thread_state_ = NULL; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | void ThreadManager::Iterate(ObjectVisitor* v) { |
| 239 | // Expecting no threads during serialization/deserialization |
| 240 | for (ThreadState* state = ThreadState::FirstInUse(); |
| 241 | state != NULL; |
| 242 | state = state->Next()) { |
| 243 | char* data = state->data(); |
| 244 | data = HandleScopeImplementer::Iterate(v, data); |
| 245 | data = Top::Iterate(v, data); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | |
| 250 | void ThreadManager::MarkCompactPrologue() { |
| 251 | for (ThreadState* state = ThreadState::FirstInUse(); |
| 252 | state != NULL; |
| 253 | state = state->Next()) { |
| 254 | char* data = state->data(); |
| 255 | data += HandleScopeImplementer::ArchiveSpacePerThread(); |
| 256 | Top::MarkCompactPrologue(data); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | |
| 261 | void ThreadManager::MarkCompactEpilogue() { |
| 262 | for (ThreadState* state = ThreadState::FirstInUse(); |
| 263 | state != NULL; |
| 264 | state = state->Next()) { |
| 265 | char* data = state->data(); |
| 266 | data += HandleScopeImplementer::ArchiveSpacePerThread(); |
| 267 | Top::MarkCompactEpilogue(data); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | |
| 272 | ContextSwitcher::ContextSwitcher(int every_n_ms) |
| 273 | : preemption_semaphore_(OS::CreateSemaphore(0)), |
| 274 | keep_going_(true), |
| 275 | sleep_ms_(every_n_ms) { |
| 276 | } |
| 277 | |
| 278 | |
| 279 | static v8::internal::ContextSwitcher* switcher; |
| 280 | |
| 281 | |
| 282 | void ContextSwitcher::StartPreemption(int every_n_ms) { |
ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 283 | ASSERT(Locker::IsLocked()); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 284 | if (switcher == NULL) { |
| 285 | switcher = new ContextSwitcher(every_n_ms); |
| 286 | switcher->Start(); |
| 287 | } else { |
| 288 | switcher->sleep_ms_ = every_n_ms; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | |
| 293 | void ContextSwitcher::StopPreemption() { |
ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 294 | ASSERT(Locker::IsLocked()); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 295 | if (switcher != NULL) { |
| 296 | switcher->Stop(); |
| 297 | delete(switcher); |
| 298 | switcher = NULL; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | |
| 303 | void ContextSwitcher::Run() { |
| 304 | while (keep_going_) { |
| 305 | OS::Sleep(sleep_ms_); |
| 306 | StackGuard::Preempt(); |
| 307 | WaitForPreemption(); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | |
| 312 | void ContextSwitcher::Stop() { |
ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 313 | ASSERT(Locker::IsLocked()); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 314 | keep_going_ = false; |
| 315 | preemption_semaphore_->Signal(); |
| 316 | Join(); |
| 317 | } |
| 318 | |
| 319 | |
| 320 | void ContextSwitcher::WaitForPreemption() { |
| 321 | preemption_semaphore_->Wait(); |
| 322 | } |
| 323 | |
| 324 | |
| 325 | void ContextSwitcher::PreemptionReceived() { |
ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 326 | ASSERT(Locker::IsLocked()); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 327 | switcher->preemption_semaphore_->Signal(); |
| 328 | } |
| 329 | |
| 330 | |
| 331 | } // namespace internal |
| 332 | } // namespace v8 |