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