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
17namespace v8 {
18
19namespace {
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.
23base::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.
29void 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
53bool 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
60bool Locker::IsActive() { return WasEverUsed(); }
61
62// static
63bool Locker::WasEverUsed() {
64  return base::Relaxed_Load(&g_locker_was_ever_used_) != 0;
65}
66
67Locker::~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
79void 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
87Unlocker::~Unlocker() {
88  DCHECK(!isolate_->thread_manager()->IsLockedByCurrentThread());
89  isolate_->thread_manager()->Lock();
90  isolate_->thread_manager()->RestoreThread();
91}
92
93namespace internal {
94
95void ThreadManager::InitThread(const ExecutionAccess& lock) {
96  isolate_->InitializeThreadLocal();
97  isolate_->stack_guard()->InitThread(lock);
98  isolate_->debug()->InitThread(lock);
99}
100
101bool 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
153void ThreadManager::Lock() {
154  mutex_.Lock();
155  mutex_owner_.store(ThreadId::Current(), std::memory_order_relaxed);
156  DCHECK(IsLockedByCurrentThread());
157}
158
159void ThreadManager::Unlock() {
160  mutex_owner_.store(ThreadId::Invalid(), std::memory_order_relaxed);
161  mutex_.Unlock();
162}
163
164static 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
173ThreadState::ThreadState(ThreadManager* thread_manager)
174    : id_(ThreadId::Invalid()),
175      data_(nullptr),
176      next_(this),
177      previous_(this),
178      thread_manager_(thread_manager) {}
179
180ThreadState::~ThreadState() { DeleteArray<char>(data_); }
181
182void ThreadState::AllocateSpace() {
183  data_ = NewArray<char>(ArchiveSpacePerThread());
184}
185
186void ThreadState::Unlink() {
187  next_->previous_ = previous_;
188  previous_->next_ = next_;
189}
190
191void 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
201ThreadState* 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.
212ThreadState* ThreadManager::FirstThreadStateInUse() {
213  return in_use_anchor_->Next();
214}
215
216ThreadState* 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.)
224ThreadManager::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
235ThreadManager::~ThreadManager() {
236  DeleteThreadStateList(free_anchor_);
237  DeleteThreadStateList(in_use_anchor_);
238}
239
240void 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
250void 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
266void 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
284void 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
296bool ThreadManager::IsArchived() {
297  Isolate::PerIsolateThreadData* data =
298      isolate_->FindPerThreadDataForThisThread();
299  return data != nullptr && data->thread_state() != nullptr;
300}
301
302void 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
315void 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
324ThreadId ThreadManager::CurrentId() { return ThreadId::Current(); }
325
326}  // namespace internal
327}  // namespace v8
328