Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / workers / JSExecutionManager.h
blob132884bf53fd2918b956e7aa2d089efa0f139154
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__
10 #include <stdint.h>
11 #include <deque>
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;
21 namespace mozilla {
23 class ErrorResult;
25 namespace dom {
26 class WorkerPrivate;
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
41 // on the stack.
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 {
66 public:
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();
79 private:
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
115 // by this manager.
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) =
135 false;
138 // Helper for managing execution requests and allowing re-entrant permission
139 // requests.
140 class MOZ_STACK_CLASS AutoRequestJSThreadExecution {
141 public:
142 explicit AutoRequestJSThreadExecution(nsIGlobalObject* aGlobalObject,
143 bool aIsMainThread);
145 ~AutoRequestJSThreadExecution() {
146 if (mExecutionGrantingManager) {
147 mExecutionGrantingManager->YieldJSThreadExecution();
149 if (mIsMainThread) {
150 if (mOldGrantingManager) {
151 mOldGrantingManager->RequestJSThreadExecution();
153 JSExecutionManager::mCurrentMTManager = mOldGrantingManager;
157 private:
158 // The manager we obtained permission from. nullptr if permission was already
159 // granted.
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.
166 bool mIsMainThread;
169 // Class used to wrap code which essentially exits JS execution and may block
170 // on other threads.
171 class MOZ_STACK_CLASS AutoYieldJSThreadExecution {
172 public:
173 AutoYieldJSThreadExecution();
175 ~AutoYieldJSThreadExecution() {
176 if (mExecutionGrantingManager) {
177 mExecutionGrantingManager->RequestJSThreadExecution();
178 if (NS_IsMainThread()) {
179 JSExecutionManager::mCurrentMTManager = mExecutionGrantingManager;
184 private:
185 // Set to the granting manager if we were granted permission here.
186 RefPtr<JSExecutionManager> mExecutionGrantingManager;
189 } // namespace dom
190 } // namespace mozilla
192 #endif // mozilla_dom_workers_jsexecutionmanager_h__