1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_workers_jsexecutionmanager_h__
8 #define mozilla_dom_workers_jsexecutionmanager_h__
12 #include "MainThreadUtils.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/CondVar.h"
16 #include "mozilla/Mutex.h"
17 #include "mozilla/RefPtr.h"
18 #include "nsISupports.h"
20 class nsIGlobalObject
;
28 // The code in this file is responsible for throttling JS execution. It does
29 // this by introducing a JSExecutionManager class. An execution manager may be
30 // applied to any number of worker threads or a DocGroup on the main thread.
32 // JS environments associated with a JS execution manager may only execute on a
33 // certain amount of CPU cores in parallel.
35 // Whenever the main thread, or a worker thread begins executing JS it should
36 // make sure AutoRequestJSThreadExecution is present on the stack, in practice
37 // this is done by it being part of AutoEntryScript.
39 // Whenever the main thread may end up blocking on the activity of a worker
40 // thread, it should make sure to have an AutoYieldJSThreadExecution object
43 // Whenever a worker thread may end up blocking on the main thread or the
44 // activity of another worker thread, it should make sure to have an
45 // AutoYieldJSThreadExecution object on the stack.
47 // Failure to do this may result in a deadlock. When a deadlock occurs due
48 // to these circumstances we will crash after 20 seconds.
50 // For the main thread this class should only be used in the case of an
51 // emergency surrounding exploitability of SharedArrayBuffers. A single
52 // execution manager will then be shared between all Workers and the main
53 // thread doc group containing the SharedArrayBuffer and ensure all this code
54 // only runs in a serialized manner. On the main thread we therefore may have
55 // 1 execution manager per DocGroup, as this is the granularity at which
56 // SharedArrayBuffers may be present.
58 class AutoRequestJSThreadExecution
;
59 class AutoYieldJSThreadExecution
;
61 // This class is used to regulate JS execution when for whatever reason we wish
62 // to throttle execution of multiple JS environments to occur with a certain
63 // maximum of synchronously executing threads. This should be used through
64 // the stack helper classes.
65 class JSExecutionManager
{
67 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JSExecutionManager
)
69 explicit JSExecutionManager(int32_t aMaxRunning
= 1)
70 : mMaxRunning(aMaxRunning
) {}
72 enum class RequestState
{ Granted
, ExecutingAlready
};
74 static void Initialize();
75 static void Shutdown();
77 static JSExecutionManager
* GetSABSerializationManager();
80 friend class AutoRequestJSThreadExecution
;
81 friend class AutoYieldJSThreadExecution
;
82 ~JSExecutionManager() = default;
84 // Methods used by Auto*JSThreadExecution
86 // Request execution permission, returns ExecutingAlready if execution was
87 // already granted or does not apply to this thread.
88 RequestState
RequestJSThreadExecution();
90 // Yield JS execution, this asserts that permission is actually granted.
91 void YieldJSThreadExecution();
93 // Yield JS execution if permission was granted. This returns false if no
94 // permission is granted. This method is needed because an execution manager
95 // may have been set in between the ctor and dtor of
96 // AutoYieldJSThreadExecution.
97 bool YieldJSThreadExecutionIfGranted();
99 RequestState
RequestJSThreadExecutionMainThread();
101 // Execution manager currently managing the main thread.
102 // MainThread access only.
103 static JSExecutionManager
* mCurrentMTManager
;
105 // Workers waiting to be given permission for execution.
106 // Guarded by mExecutionQueueMutex.
107 std::deque
<WorkerPrivate
*> mExecutionQueue
108 MOZ_GUARDED_BY(mExecutionQueueMutex
);
110 // Number of threads currently executing concurrently for this manager.
111 // Guarded by mExecutionQueueMutex.
112 int32_t mRunning
MOZ_GUARDED_BY(mExecutionQueueMutex
) = 0;
114 // Number of threads allowed to run concurrently for environments managed
116 // Guarded by mExecutionQueueMutex.
117 int32_t mMaxRunning
MOZ_GUARDED_BY(mExecutionQueueMutex
) = 1;
119 // Mutex that guards the execution queue and associated state.
120 Mutex mExecutionQueueMutex
=
121 Mutex
{"JSExecutionManager::sExecutionQueueMutex"};
123 // ConditionVariables that blocked threads wait for.
124 CondVar mExecutionQueueCondVar
=
125 CondVar
{mExecutionQueueMutex
, "JSExecutionManager::sExecutionQueueMutex"};
127 // Whether the main thread is currently executing for this manager.
128 // MainThread access only.
129 bool mMainThreadIsExecuting
= false;
131 // Whether the main thread is currently awaiting permission to execute. Main
132 // thread execution is always prioritized.
133 // Guarded by mExecutionQueueMutex.
134 bool mMainThreadAwaitingExecution
MOZ_GUARDED_BY(mExecutionQueueMutex
) =
138 // Helper for managing execution requests and allowing re-entrant permission
140 class MOZ_STACK_CLASS AutoRequestJSThreadExecution
{
142 explicit AutoRequestJSThreadExecution(nsIGlobalObject
* aGlobalObject
,
145 ~AutoRequestJSThreadExecution() {
146 if (mExecutionGrantingManager
) {
147 mExecutionGrantingManager
->YieldJSThreadExecution();
150 if (mOldGrantingManager
) {
151 mOldGrantingManager
->RequestJSThreadExecution();
153 JSExecutionManager::mCurrentMTManager
= mOldGrantingManager
;
158 // The manager we obtained permission from. nullptr if permission was already
160 RefPtr
<JSExecutionManager
> mExecutionGrantingManager
;
161 // The manager we had permission from before, and where permission should be
162 // re-requested upon destruction.
163 RefPtr
<JSExecutionManager
> mOldGrantingManager
;
165 // We store this for performance reasons.
169 // Class used to wrap code which essentially exits JS execution and may block
171 class MOZ_STACK_CLASS AutoYieldJSThreadExecution
{
173 AutoYieldJSThreadExecution();
175 ~AutoYieldJSThreadExecution() {
176 if (mExecutionGrantingManager
) {
177 mExecutionGrantingManager
->RequestJSThreadExecution();
178 if (NS_IsMainThread()) {
179 JSExecutionManager::mCurrentMTManager
= mExecutionGrantingManager
;
185 // Set to the granting manager if we were granted permission here.
186 RefPtr
<JSExecutionManager
> mExecutionGrantingManager
;
190 } // namespace mozilla
192 #endif // mozilla_dom_workers_jsexecutionmanager_h__