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