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/v8.h"
6
7#include "src/api.h"
8#include "src/bootstrapper.h"
9#include "src/debug.h"
10#include "src/execution.h"
11#include "src/regexp-stack.h"
12#include "src/v8threads.h"
13
14namespace v8 {
15
16
17// Track whether this V8 instance has ever called v8::Locker. This allows the
18// API code to verify that the lock is always held when V8 is being entered.
19bool Locker::active_ = false;
20
21
22// Once the Locker is initialized, the current thread will be guaranteed to have
23// the lock for a given isolate.
24void Locker::Initialize(v8::Isolate* isolate) {
25  DCHECK(isolate != NULL);
26  has_lock_= false;
27  top_level_ = true;
28  isolate_ = reinterpret_cast<i::Isolate*>(isolate);
29  // Record that the Locker has been used at least once.
30  active_ = true;
31  // Get the big lock if necessary.
32  if (!isolate_->thread_manager()->IsLockedByCurrentThread()) {
33    isolate_->thread_manager()->Lock();
34    has_lock_ = true;
35
36    // Make sure that V8 is initialized.  Archiving of threads interferes
37    // with deserialization by adding additional root pointers, so we must
38    // initialize here, before anyone can call ~Locker() or Unlocker().
39    if (!isolate_->IsInitialized()) {
40      isolate_->Enter();
41      V8::Initialize();
42      isolate_->Exit();
43    }
44
45    // This may be a locker within an unlocker in which case we have to
46    // get the saved state for this thread and restore it.
47    if (isolate_->thread_manager()->RestoreThread()) {
48      top_level_ = false;
49    } else {
50      internal::ExecutionAccess access(isolate_);
51      isolate_->stack_guard()->ClearThread(access);
52      isolate_->stack_guard()->InitThread(access);
53    }
54  }
55  DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
56}
57
58
59bool Locker::IsLocked(v8::Isolate* isolate) {
60  DCHECK(isolate != NULL);
61  i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
62  return internal_isolate->thread_manager()->IsLockedByCurrentThread();
63}
64
65
66bool Locker::IsActive() {
67  return active_;
68}
69
70
71Locker::~Locker() {
72  DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
73  if (has_lock_) {
74    if (top_level_) {
75      isolate_->thread_manager()->FreeThreadResources();
76    } else {
77      isolate_->thread_manager()->ArchiveThread();
78    }
79    isolate_->thread_manager()->Unlock();
80  }
81}
82
83
84void Unlocker::Initialize(v8::Isolate* isolate) {
85  DCHECK(isolate != NULL);
86  isolate_ = reinterpret_cast<i::Isolate*>(isolate);
87  DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
88  isolate_->thread_manager()->ArchiveThread();
89  isolate_->thread_manager()->Unlock();
90}
91
92
93Unlocker::~Unlocker() {
94  DCHECK(!isolate_->thread_manager()->IsLockedByCurrentThread());
95  isolate_->thread_manager()->Lock();
96  isolate_->thread_manager()->RestoreThread();
97}
98
99
100namespace internal {
101
102
103bool 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_.Equals(ThreadId::Current())) {
109    lazily_archived_thread_ = ThreadId::Invalid();
110    Isolate::PerIsolateThreadData* per_thread =
111        isolate_->FindPerThreadDataForThisThread();
112    DCHECK(per_thread != NULL);
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_ = NULL;
117    per_thread->set_thread_state(NULL);
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 == NULL || per_thread->thread_state() == NULL) {
133    // This is a new thread.
134    isolate_->stack_guard()->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(NULL);
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
158void ThreadManager::Lock() {
159  mutex_.Lock();
160  mutex_owner_ = ThreadId::Current();
161  DCHECK(IsLockedByCurrentThread());
162}
163
164
165void ThreadManager::Unlock() {
166  mutex_owner_ = ThreadId::Invalid();
167  mutex_.Unlock();
168}
169
170
171static 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
182ThreadState::ThreadState(ThreadManager* thread_manager)
183    : id_(ThreadId::Invalid()),
184      terminate_on_restore_(false),
185      data_(NULL),
186      next_(this),
187      previous_(this),
188      thread_manager_(thread_manager) {
189}
190
191
192ThreadState::~ThreadState() {
193  DeleteArray<char>(data_);
194}
195
196
197void ThreadState::AllocateSpace() {
198  data_ = NewArray<char>(ArchiveSpacePerThread());
199}
200
201
202void ThreadState::Unlink() {
203  next_->previous_ = previous_;
204  previous_->next_ = next_;
205}
206
207
208void ThreadState::LinkInto(List list) {
209  ThreadState* flying_anchor =
210      list == FREE_LIST ? thread_manager_->free_anchor_
211                        : thread_manager_->in_use_anchor_;
212  next_ = flying_anchor->next_;
213  previous_ = flying_anchor;
214  flying_anchor->next_ = this;
215  next_->previous_ = this;
216}
217
218
219ThreadState* ThreadManager::GetFreeThreadState() {
220  ThreadState* gotten = free_anchor_->next_;
221  if (gotten == free_anchor_) {
222    ThreadState* new_thread_state = new ThreadState(this);
223    new_thread_state->AllocateSpace();
224    return new_thread_state;
225  }
226  return gotten;
227}
228
229
230// Gets the first in the list of archived threads.
231ThreadState* ThreadManager::FirstThreadStateInUse() {
232  return in_use_anchor_->Next();
233}
234
235
236ThreadState* ThreadState::Next() {
237  if (next_ == thread_manager_->in_use_anchor_) return NULL;
238  return next_;
239}
240
241
242// Thread ids must start with 1, because in TLS having thread id 0 can't
243// be distinguished from not having a thread id at all (since NULL is
244// defined as 0.)
245ThreadManager::ThreadManager()
246    : mutex_owner_(ThreadId::Invalid()),
247      lazily_archived_thread_(ThreadId::Invalid()),
248      lazily_archived_thread_state_(NULL),
249      free_anchor_(NULL),
250      in_use_anchor_(NULL) {
251  free_anchor_ = new ThreadState(this);
252  in_use_anchor_ = new ThreadState(this);
253}
254
255
256ThreadManager::~ThreadManager() {
257  DeleteThreadStateList(free_anchor_);
258  DeleteThreadStateList(in_use_anchor_);
259}
260
261
262void ThreadManager::DeleteThreadStateList(ThreadState* anchor) {
263  // The list starts and ends with the anchor.
264  for (ThreadState* current = anchor->next_; current != anchor;) {
265    ThreadState* next = current->next_;
266    delete current;
267    current = next;
268  }
269  delete anchor;
270}
271
272
273void ThreadManager::ArchiveThread() {
274  DCHECK(lazily_archived_thread_.Equals(ThreadId::Invalid()));
275  DCHECK(!IsArchived());
276  DCHECK(IsLockedByCurrentThread());
277  ThreadState* state = GetFreeThreadState();
278  state->Unlink();
279  Isolate::PerIsolateThreadData* per_thread =
280      isolate_->FindOrAllocatePerThreadDataForThisThread();
281  per_thread->set_thread_state(state);
282  lazily_archived_thread_ = ThreadId::Current();
283  lazily_archived_thread_state_ = state;
284  DCHECK(state->id().Equals(ThreadId::Invalid()));
285  state->set_id(CurrentId());
286  DCHECK(!state->id().Equals(ThreadId::Invalid()));
287}
288
289
290void ThreadManager::EagerlyArchiveThread() {
291  DCHECK(IsLockedByCurrentThread());
292  ThreadState* state = lazily_archived_thread_state_;
293  state->LinkInto(ThreadState::IN_USE_LIST);
294  char* to = state->data();
295  // Ensure that data containing GC roots are archived first, and handle them
296  // in ThreadManager::Iterate(ObjectVisitor*).
297  to = isolate_->handle_scope_implementer()->ArchiveThread(to);
298  to = isolate_->ArchiveThread(to);
299  to = Relocatable::ArchiveState(isolate_, to);
300  to = isolate_->debug()->ArchiveDebug(to);
301  to = isolate_->stack_guard()->ArchiveStackGuard(to);
302  to = isolate_->regexp_stack()->ArchiveStack(to);
303  to = isolate_->bootstrapper()->ArchiveState(to);
304  lazily_archived_thread_ = ThreadId::Invalid();
305  lazily_archived_thread_state_ = NULL;
306}
307
308
309void ThreadManager::FreeThreadResources() {
310  DCHECK(!isolate_->has_pending_exception());
311  DCHECK(!isolate_->external_caught_exception());
312  DCHECK(isolate_->try_catch_handler() == NULL);
313  isolate_->handle_scope_implementer()->FreeThreadResources();
314  isolate_->FreeThreadResources();
315  isolate_->debug()->FreeThreadResources();
316  isolate_->stack_guard()->FreeThreadResources();
317  isolate_->regexp_stack()->FreeThreadResources();
318  isolate_->bootstrapper()->FreeThreadResources();
319}
320
321
322bool ThreadManager::IsArchived() {
323  Isolate::PerIsolateThreadData* data =
324      isolate_->FindPerThreadDataForThisThread();
325  return data != NULL && data->thread_state() != NULL;
326}
327
328
329void ThreadManager::Iterate(ObjectVisitor* v) {
330  // Expecting no threads during serialization/deserialization
331  for (ThreadState* state = FirstThreadStateInUse();
332       state != NULL;
333       state = state->Next()) {
334    char* data = state->data();
335    data = HandleScopeImplementer::Iterate(v, data);
336    data = isolate_->Iterate(v, data);
337    data = Relocatable::Iterate(v, data);
338  }
339}
340
341
342void ThreadManager::IterateArchivedThreads(ThreadVisitor* v) {
343  for (ThreadState* state = FirstThreadStateInUse();
344       state != NULL;
345       state = state->Next()) {
346    char* data = state->data();
347    data += HandleScopeImplementer::ArchiveSpacePerThread();
348    isolate_->IterateThread(v, data);
349  }
350}
351
352
353ThreadId ThreadManager::CurrentId() {
354  return ThreadId::Current();
355}
356
357
358void ThreadManager::TerminateExecution(ThreadId thread_id) {
359  for (ThreadState* state = FirstThreadStateInUse();
360       state != NULL;
361       state = state->Next()) {
362    if (thread_id.Equals(state->id())) {
363      state->set_terminate_on_restore(true);
364    }
365  }
366}
367
368
369}  // namespace internal
370}  // namespace v8
371