1 // Copyright 2021 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 #ifndef INCLUDE_V8_LOCKER_H_
6 #define INCLUDE_V8_LOCKER_H_
7 
8 #include "v8config.h"  // NOLINT(build/include_directory)
9 
10 namespace v8 {
11 
12 namespace internal {
13 class Isolate;
14 }  // namespace internal
15 
16 class Isolate;
17 
18 /**
19  * Multiple threads in V8 are allowed, but only one thread at a time is allowed
20  * to use any given V8 isolate, see the comments in the Isolate class. The
21  * definition of 'using a V8 isolate' includes accessing handles or holding onto
22  * object pointers obtained from V8 handles while in the particular V8 isolate.
23  * It is up to the user of V8 to ensure, perhaps with locking, that this
24  * constraint is not violated. In addition to any other synchronization
25  * mechanism that may be used, the v8::Locker and v8::Unlocker classes must be
26  * used to signal thread switches to V8.
27  *
28  * v8::Locker is a scoped lock object. While it's active, i.e. between its
29  * construction and destruction, the current thread is allowed to use the locked
30  * isolate. V8 guarantees that an isolate can be locked by at most one thread at
31  * any time. In other words, the scope of a v8::Locker is a critical section.
32  *
33  * Sample usage:
34  * \code
35  * ...
36  * {
37  *   v8::Locker locker(isolate);
38  *   v8::Isolate::Scope isolate_scope(isolate);
39  *   ...
40  *   // Code using V8 and isolate goes here.
41  *   ...
42  * } // Destructor called here
43  * \endcode
44  *
45  * If you wish to stop using V8 in a thread A you can do this either by
46  * destroying the v8::Locker object as above or by constructing a v8::Unlocker
47  * object:
48  *
49  * \code
50  * {
51  *   isolate->Exit();
52  *   v8::Unlocker unlocker(isolate);
53  *   ...
54  *   // Code not using V8 goes here while V8 can run in another thread.
55  *   ...
56  * } // Destructor called here.
57  * isolate->Enter();
58  * \endcode
59  *
60  * The Unlocker object is intended for use in a long-running callback from V8,
61  * where you want to release the V8 lock for other threads to use.
62  *
63  * The v8::Locker is a recursive lock, i.e. you can lock more than once in a
64  * given thread. This can be useful if you have code that can be called either
65  * from code that holds the lock or from code that does not. The Unlocker is
66  * not recursive so you can not have several Unlockers on the stack at once, and
67  * you cannot use an Unlocker in a thread that is not inside a Locker's scope.
68  *
69  * An unlocker will unlock several lockers if it has to and reinstate the
70  * correct depth of locking on its destruction, e.g.:
71  *
72  * \code
73  * // V8 not locked.
74  * {
75  *   v8::Locker locker(isolate);
76  *   Isolate::Scope isolate_scope(isolate);
77  *   // V8 locked.
78  *   {
79  *     v8::Locker another_locker(isolate);
80  *     // V8 still locked (2 levels).
81  *     {
82  *       isolate->Exit();
83  *       v8::Unlocker unlocker(isolate);
84  *       // V8 not locked.
85  *     }
86  *     isolate->Enter();
87  *     // V8 locked again (2 levels).
88  *   }
89  *   // V8 still locked (1 level).
90  * }
91  * // V8 Now no longer locked.
92  * \endcode
93  */
94 class V8_EXPORT Unlocker {
95  public:
96   /**
97    * Initialize Unlocker for a given Isolate.
98    */
Unlocker(Isolate * isolate)99   V8_INLINE explicit Unlocker(Isolate* isolate) { Initialize(isolate); }
100 
101   ~Unlocker();
102 
103  private:
104   void Initialize(Isolate* isolate);
105 
106   internal::Isolate* isolate_;
107 };
108 
109 class V8_EXPORT Locker {
110  public:
111   /**
112    * Initialize Locker for a given Isolate.
113    */
Locker(Isolate * isolate)114   V8_INLINE explicit Locker(Isolate* isolate) { Initialize(isolate); }
115 
116   ~Locker();
117 
118   /**
119    * Returns whether or not the locker for a given isolate, is locked by the
120    * current thread.
121    */
122   static bool IsLocked(Isolate* isolate);
123 
124   /**
125    * Returns whether any v8::Locker has ever been used in this process.
126    * TODO(cbruni, chromium:1240851): Fix locking checks on a per-thread basis.
127    * The current implementation is quite confusing and leads to unexpected
128    * results if anybody uses v8::Locker in the current process.
129    */
130   static bool WasEverUsed();
131   V8_DEPRECATE_SOON("Use WasEverUsed instead")
132   static bool IsActive();
133 
134   // Disallow copying and assigning.
135   Locker(const Locker&) = delete;
136   void operator=(const Locker&) = delete;
137 
138  private:
139   void Initialize(Isolate* isolate);
140 
141   bool has_lock_;
142   bool top_level_;
143   internal::Isolate* isolate_;
144 };
145 
146 }  // namespace v8
147 
148 #endif  // INCLUDE_V8_LOCKER_H_
149