1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/execution/v8threads.h"
6
7 #include "include/v8-locker.h"
8 #include "src/api/api.h"
9 #include "src/debug/debug.h"
10 #include "src/execution/execution.h"
11 #include "src/execution/isolate-inl.h"
12 #include "src/execution/stack-guard.h"
13 #include "src/init/bootstrapper.h"
14 #include "src/objects/visitors.h"
15 #include "src/regexp/regexp-stack.h"
16
17 namespace v8 {
18
19 namespace {
20
21 // Track whether this V8 instance has ever called v8::Locker. This allows the
22 // API code to verify that the lock is always held when V8 is being entered.
23 base::AtomicWord g_locker_was_ever_used_ = 0;
24
25 } // namespace
26
27 // Once the Locker is initialized, the current thread will be guaranteed to have
28 // the lock for a given isolate.
Initialize(v8::Isolate * isolate)29 void Locker::Initialize(v8::Isolate* isolate) {
30 DCHECK_NOT_NULL(isolate);
31 has_lock_ = false;
32 top_level_ = true;
33 isolate_ = reinterpret_cast<i::Isolate*>(isolate);
34
35 // Record that the Locker has been used at least once.
36 base::Relaxed_Store(&g_locker_was_ever_used_, 1);
37 isolate_->set_was_locker_ever_used();
38
39 // Get the big lock if necessary.
40 if (!isolate_->thread_manager()->IsLockedByCurrentThread()) {
41 isolate_->thread_manager()->Lock();
42 has_lock_ = true;
43
44 // This may be a locker within an unlocker in which case we have to
45 // get the saved state for this thread and restore it.
46 if (isolate_->thread_manager()->RestoreThread()) {
47 top_level_ = false;
48 }
49 }
50 DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
51 }
52
IsLocked(v8::Isolate * isolate)53 bool Locker::IsLocked(v8::Isolate* isolate) {
54 DCHECK_NOT_NULL(isolate);
55 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
56 return internal_isolate->thread_manager()->IsLockedByCurrentThread();
57 }
58
59 // static
IsActive()60 bool Locker::IsActive() { return WasEverUsed(); }
61
62 // static
WasEverUsed()63 bool Locker::WasEverUsed() {
64 return base::Relaxed_Load(&g_locker_was_ever_used_) != 0;
65 }
66
~Locker()67 Locker::~Locker() {
68 DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
69 if (has_lock_) {
70 if (top_level_) {
71 isolate_->thread_manager()->FreeThreadResources();
72 } else {
73 isolate_->thread_manager()->ArchiveThread();
74 }
75 isolate_->thread_manager()->Unlock();
76 }
77 }
78
Initialize(v8::Isolate * isolate)79 void Unlocker::Initialize(v8::Isolate* isolate) {
80 DCHECK_NOT_NULL(isolate);
81 isolate_ = reinterpret_cast<i::Isolate*>(isolate);
82 DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
83 isolate_->thread_manager()->ArchiveThread();
84 isolate_->thread_manager()->Unlock();
85 }
86
~Unlocker()87 Unlocker::~Unlocker() {
88 DCHECK(!isolate_->thread_manager()->IsLockedByCurrentThread());
89 isolate_->thread_manager()->Lock();
90 isolate_->thread_manager()->RestoreThread();
91 }
92
93 namespace internal {
94
InitThread(const ExecutionAccess & lock)95 void ThreadManager::InitThread(const ExecutionAccess& lock) {
96 isolate_->InitializeThreadLocal();
97 isolate_->stack_guard()->InitThread(lock);
98 isolate_->debug()->InitThread(lock);
99 }
100
RestoreThread()101 bool ThreadManager::RestoreThread() {
102 DCHECK(IsLockedByCurrentThread());
103 // First check whether the current thread has been 'lazily archived', i.e.
104 // not archived at all. If that is the case we put the state storage we
105 // had prepared back in the free list, since we didn't need it after all.
106 if (lazily_archived_thread_ == ThreadId::Current()) {
107 lazily_archived_thread_ = ThreadId::Invalid();
108 Isolate::PerIsolateThreadData* per_thread =
109 isolate_->FindPerThreadDataForThisThread();
110 DCHECK_NOT_NULL(per_thread);
111 DCHECK(per_thread->thread_state() == lazily_archived_thread_state_);
112 lazily_archived_thread_state_->set_id(ThreadId::Invalid());
113 lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
114 lazily_archived_thread_state_ = nullptr;
115 per_thread->set_thread_state(nullptr);
116 return true;
117 }
118
119 // Make sure that the preemption thread cannot modify the thread state while
120 // it is being archived or restored.
121 ExecutionAccess access(isolate_);
122
123 // If there is another thread that was lazily archived then we have to really
124 // archive it now.
125 if (lazily_archived_thread_.IsValid()) {
126 EagerlyArchiveThread();
127 }
128 Isolate::PerIsolateThreadData* per_thread =
129 isolate_->FindPerThreadDataForThisThread();
130 if (per_thread == nullptr || per_thread->thread_state() == nullptr) {
131 // This is a new thread.
132 InitThread(access);
133 return false;
134 }
135 ThreadState* state = per_thread->thread_state();
136 char* from = state->data();
137 from = isolate_->handle_scope_implementer()->RestoreThread(from);
138 from = isolate_->RestoreThread(from);
139 from = Relocatable::RestoreState(isolate_, from);
140 // Stack guard should be restored before Debug, etc. since Debug etc. might
141 // depend on a correct stack guard.
142 from = isolate_->stack_guard()->RestoreStackGuard(from);
143 from = isolate_->debug()->RestoreDebug(from);
144 from = isolate_->regexp_stack()->RestoreStack(from);
145 from = isolate_->bootstrapper()->RestoreState(from);
146 per_thread->set_thread_state(nullptr);
147 state->set_id(ThreadId::Invalid());
148 state->Unlink();
149 state->LinkInto(ThreadState::FREE_LIST);
150 return true;
151 }
152
Lock()153 void ThreadManager::Lock() {
154 mutex_.Lock();
155 mutex_owner_.store(ThreadId::Current(), std::memory_order_relaxed);
156 DCHECK(IsLockedByCurrentThread());
157 }
158
Unlock()159 void ThreadManager::Unlock() {
160 mutex_owner_.store(ThreadId::Invalid(), std::memory_order_relaxed);
161 mutex_.Unlock();
162 }
163
ArchiveSpacePerThread()164 static int ArchiveSpacePerThread() {
165 return HandleScopeImplementer::ArchiveSpacePerThread() +
166 Isolate::ArchiveSpacePerThread() + Debug::ArchiveSpacePerThread() +
167 StackGuard::ArchiveSpacePerThread() +
168 RegExpStack::ArchiveSpacePerThread() +
169 Bootstrapper::ArchiveSpacePerThread() +
170 Relocatable::ArchiveSpacePerThread();
171 }
172
ThreadState(ThreadManager * thread_manager)173 ThreadState::ThreadState(ThreadManager* thread_manager)
174 : id_(ThreadId::Invalid()),
175 data_(nullptr),
176 next_(this),
177 previous_(this),
178 thread_manager_(thread_manager) {}
179
~ThreadState()180 ThreadState::~ThreadState() { DeleteArray<char>(data_); }
181
AllocateSpace()182 void ThreadState::AllocateSpace() {
183 data_ = NewArray<char>(ArchiveSpacePerThread());
184 }
185
Unlink()186 void ThreadState::Unlink() {
187 next_->previous_ = previous_;
188 previous_->next_ = next_;
189 }
190
LinkInto(List list)191 void ThreadState::LinkInto(List list) {
192 ThreadState* flying_anchor = list == FREE_LIST
193 ? thread_manager_->free_anchor_
194 : thread_manager_->in_use_anchor_;
195 next_ = flying_anchor->next_;
196 previous_ = flying_anchor;
197 flying_anchor->next_ = this;
198 next_->previous_ = this;
199 }
200
GetFreeThreadState()201 ThreadState* ThreadManager::GetFreeThreadState() {
202 ThreadState* gotten = free_anchor_->next_;
203 if (gotten == free_anchor_) {
204 ThreadState* new_thread_state = new ThreadState(this);
205 new_thread_state->AllocateSpace();
206 return new_thread_state;
207 }
208 return gotten;
209 }
210
211 // Gets the first in the list of archived threads.
FirstThreadStateInUse()212 ThreadState* ThreadManager::FirstThreadStateInUse() {
213 return in_use_anchor_->Next();
214 }
215
Next()216 ThreadState* ThreadState::Next() {
217 if (next_ == thread_manager_->in_use_anchor_) return nullptr;
218 return next_;
219 }
220
221 // Thread ids must start with 1, because in TLS having thread id 0 can't
222 // be distinguished from not having a thread id at all (since NULL is
223 // defined as 0.)
ThreadManager(Isolate * isolate)224 ThreadManager::ThreadManager(Isolate* isolate)
225 : mutex_owner_(ThreadId::Invalid()),
226 lazily_archived_thread_(ThreadId::Invalid()),
227 lazily_archived_thread_state_(nullptr),
228 free_anchor_(nullptr),
229 in_use_anchor_(nullptr),
230 isolate_(isolate) {
231 free_anchor_ = new ThreadState(this);
232 in_use_anchor_ = new ThreadState(this);
233 }
234
~ThreadManager()235 ThreadManager::~ThreadManager() {
236 DeleteThreadStateList(free_anchor_);
237 DeleteThreadStateList(in_use_anchor_);
238 }
239
DeleteThreadStateList(ThreadState * anchor)240 void ThreadManager::DeleteThreadStateList(ThreadState* anchor) {
241 // The list starts and ends with the anchor.
242 for (ThreadState* current = anchor->next_; current != anchor;) {
243 ThreadState* next = current->next_;
244 delete current;
245 current = next;
246 }
247 delete anchor;
248 }
249
ArchiveThread()250 void ThreadManager::ArchiveThread() {
251 DCHECK_EQ(lazily_archived_thread_, ThreadId::Invalid());
252 DCHECK(!IsArchived());
253 DCHECK(IsLockedByCurrentThread());
254 ThreadState* state = GetFreeThreadState();
255 state->Unlink();
256 Isolate::PerIsolateThreadData* per_thread =
257 isolate_->FindOrAllocatePerThreadDataForThisThread();
258 per_thread->set_thread_state(state);
259 lazily_archived_thread_ = ThreadId::Current();
260 lazily_archived_thread_state_ = state;
261 DCHECK_EQ(state->id(), ThreadId::Invalid());
262 state->set_id(CurrentId());
263 DCHECK_NE(state->id(), ThreadId::Invalid());
264 }
265
EagerlyArchiveThread()266 void ThreadManager::EagerlyArchiveThread() {
267 DCHECK(IsLockedByCurrentThread());
268 ThreadState* state = lazily_archived_thread_state_;
269 state->LinkInto(ThreadState::IN_USE_LIST);
270 char* to = state->data();
271 // Ensure that data containing GC roots are archived first, and handle them
272 // in ThreadManager::Iterate(RootVisitor*).
273 to = isolate_->handle_scope_implementer()->ArchiveThread(to);
274 to = isolate_->ArchiveThread(to);
275 to = Relocatable::ArchiveState(isolate_, to);
276 to = isolate_->stack_guard()->ArchiveStackGuard(to);
277 to = isolate_->debug()->ArchiveDebug(to);
278 to = isolate_->regexp_stack()->ArchiveStack(to);
279 to = isolate_->bootstrapper()->ArchiveState(to);
280 lazily_archived_thread_ = ThreadId::Invalid();
281 lazily_archived_thread_state_ = nullptr;
282 }
283
FreeThreadResources()284 void ThreadManager::FreeThreadResources() {
285 DCHECK(!isolate_->has_pending_exception());
286 DCHECK(!isolate_->external_caught_exception());
287 DCHECK_NULL(isolate_->try_catch_handler());
288 isolate_->handle_scope_implementer()->FreeThreadResources();
289 isolate_->FreeThreadResources();
290 isolate_->debug()->FreeThreadResources();
291 isolate_->stack_guard()->FreeThreadResources();
292 isolate_->regexp_stack()->FreeThreadResources();
293 isolate_->bootstrapper()->FreeThreadResources();
294 }
295
IsArchived()296 bool ThreadManager::IsArchived() {
297 Isolate::PerIsolateThreadData* data =
298 isolate_->FindPerThreadDataForThisThread();
299 return data != nullptr && data->thread_state() != nullptr;
300 }
301
Iterate(RootVisitor * v)302 void ThreadManager::Iterate(RootVisitor* v) {
303 // Expecting no threads during serialization/deserialization
304 for (ThreadState* state = FirstThreadStateInUse(); state != nullptr;
305 state = state->Next()) {
306 char* data = state->data();
307 data = HandleScopeImplementer::Iterate(v, data);
308 data = isolate_->Iterate(v, data);
309 data = Relocatable::Iterate(v, data);
310 data = StackGuard::Iterate(v, data);
311 data = Debug::Iterate(v, data);
312 }
313 }
314
IterateArchivedThreads(ThreadVisitor * v)315 void ThreadManager::IterateArchivedThreads(ThreadVisitor* v) {
316 for (ThreadState* state = FirstThreadStateInUse(); state != nullptr;
317 state = state->Next()) {
318 char* data = state->data();
319 data += HandleScopeImplementer::ArchiveSpacePerThread();
320 isolate_->IterateThread(v, data);
321 }
322 }
323
CurrentId()324 ThreadId ThreadManager::CurrentId() { return ThreadId::Current(); }
325
326 } // namespace internal
327 } // namespace v8
328