Bug 1825333 - Make toolkit/components/sessionstore buildable outside of a unified...
[gecko.git] / dom / workers / JSExecutionManager.cpp
blob4a67a3a3d238c10d4ad0fc2b89a97c44f33622dd
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 #include "mozilla/dom/JSExecutionManager.h"
9 #include "WorkerCommon.h"
10 #include "WorkerPrivate.h"
12 #include "mozilla/dom/DocGroup.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 #include "mozilla/StaticPtr.h"
16 namespace mozilla::dom {
18 JSExecutionManager* JSExecutionManager::mCurrentMTManager;
20 const uint32_t kTimeSliceExpirationMS = 50;
22 using RequestState = JSExecutionManager::RequestState;
24 static StaticRefPtr<JSExecutionManager> sSABSerializationManager;
26 void JSExecutionManager::Initialize() {
27 if (StaticPrefs::dom_workers_serialized_sab_access()) {
28 sSABSerializationManager = MakeRefPtr<JSExecutionManager>(1);
32 void JSExecutionManager::Shutdown() { sSABSerializationManager = nullptr; }
34 JSExecutionManager* JSExecutionManager::GetSABSerializationManager() {
35 return sSABSerializationManager.get();
38 RequestState JSExecutionManager::RequestJSThreadExecution() {
39 if (NS_IsMainThread()) {
40 return RequestJSThreadExecutionMainThread();
43 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
45 if (!workerPrivate || workerPrivate->GetExecutionGranted()) {
46 return RequestState::ExecutingAlready;
49 MutexAutoLock lock(mExecutionQueueMutex);
50 MOZ_ASSERT(mMaxRunning >= mRunning);
52 if ((mExecutionQueue.size() + (mMainThreadAwaitingExecution ? 1 : 0)) <
53 size_t(mMaxRunning - mRunning)) {
54 // There's slots ready for things to run, execute right away.
55 workerPrivate->SetExecutionGranted(true);
56 workerPrivate->ScheduleTimeSliceExpiration(kTimeSliceExpirationMS);
58 mRunning++;
59 return RequestState::Granted;
62 mExecutionQueue.push_back(workerPrivate);
64 TimeStamp waitStart = TimeStamp::Now();
66 while (mRunning >= mMaxRunning || (workerPrivate != mExecutionQueue.front() ||
67 mMainThreadAwaitingExecution)) {
68 // If there is no slots available, the main thread is awaiting permission
69 // or we are not first in line for execution, wait until notified.
70 mExecutionQueueCondVar.Wait(TimeDuration::FromMilliseconds(500));
71 if ((TimeStamp::Now() - waitStart) > TimeDuration::FromSeconds(20)) {
72 // Crash so that these types of situations are actually caught in the
73 // crash reporter.
74 MOZ_CRASH();
78 workerPrivate->SetExecutionGranted(true);
79 workerPrivate->ScheduleTimeSliceExpiration(kTimeSliceExpirationMS);
81 mExecutionQueue.pop_front();
82 mRunning++;
83 if (mRunning < mMaxRunning) {
84 // If a thread woke up before that wasn't first in line it will have gone
85 // back to sleep, if there's more slots available, wake it now.
86 mExecutionQueueCondVar.NotifyAll();
89 return RequestState::Granted;
92 void JSExecutionManager::YieldJSThreadExecution() {
93 if (NS_IsMainThread()) {
94 MOZ_ASSERT(mMainThreadIsExecuting);
95 mMainThreadIsExecuting = false;
97 MutexAutoLock lock(mExecutionQueueMutex);
98 mRunning--;
99 mExecutionQueueCondVar.NotifyAll();
100 return;
103 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
105 if (!workerPrivate) {
106 return;
109 MOZ_ASSERT(workerPrivate->GetExecutionGranted());
111 workerPrivate->CancelTimeSliceExpiration();
113 MutexAutoLock lock(mExecutionQueueMutex);
114 mRunning--;
115 mExecutionQueueCondVar.NotifyAll();
116 workerPrivate->SetExecutionGranted(false);
119 bool JSExecutionManager::YieldJSThreadExecutionIfGranted() {
120 if (NS_IsMainThread()) {
121 if (mMainThreadIsExecuting) {
122 YieldJSThreadExecution();
123 return true;
125 return false;
128 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
130 if (workerPrivate && workerPrivate->GetExecutionGranted()) {
131 YieldJSThreadExecution();
132 return true;
135 return false;
138 RequestState JSExecutionManager::RequestJSThreadExecutionMainThread() {
139 MOZ_ASSERT(NS_IsMainThread());
141 if (mMainThreadIsExecuting) {
142 return RequestState::ExecutingAlready;
145 MutexAutoLock lock(mExecutionQueueMutex);
146 MOZ_ASSERT(mMaxRunning >= mRunning);
148 if ((mMaxRunning - mRunning) > 0) {
149 // If there's any slots available run, the main thread always takes
150 // precedence over any worker threads.
151 mRunning++;
152 mMainThreadIsExecuting = true;
153 return RequestState::Granted;
156 mMainThreadAwaitingExecution = true;
158 TimeStamp waitStart = TimeStamp::Now();
160 while (mRunning >= mMaxRunning) {
161 if ((TimeStamp::Now() - waitStart) > TimeDuration::FromSeconds(20)) {
162 // Crash so that these types of situations are actually caught in the
163 // crash reporter.
164 MOZ_CRASH();
166 mExecutionQueueCondVar.Wait(TimeDuration::FromMilliseconds(500));
169 mMainThreadAwaitingExecution = false;
170 mMainThreadIsExecuting = true;
172 mRunning++;
173 if (mRunning < mMaxRunning) {
174 // If a thread woke up before that wasn't first in line it will have gone
175 // back to sleep, if there's more slots available, wake it now.
176 mExecutionQueueCondVar.NotifyAll();
179 return RequestState::Granted;
182 AutoRequestJSThreadExecution::AutoRequestJSThreadExecution(
183 nsIGlobalObject* aGlobalObject, bool aIsMainThread) {
184 JSExecutionManager* manager = nullptr;
186 mIsMainThread = aIsMainThread;
187 if (mIsMainThread) {
188 mOldGrantingManager = JSExecutionManager::mCurrentMTManager;
190 nsPIDOMWindowInner* innerWindow = nullptr;
191 if (aGlobalObject) {
192 innerWindow = aGlobalObject->AsInnerWindow();
195 DocGroup* docGroup = nullptr;
196 if (innerWindow) {
197 docGroup = innerWindow->GetDocGroup();
200 if (docGroup) {
201 manager = docGroup->GetExecutionManager();
204 if (JSExecutionManager::mCurrentMTManager == manager) {
205 return;
208 if (JSExecutionManager::mCurrentMTManager) {
209 JSExecutionManager::mCurrentMTManager->YieldJSThreadExecution();
210 JSExecutionManager::mCurrentMTManager = nullptr;
212 } else {
213 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
215 if (workerPrivate) {
216 manager = workerPrivate->GetExecutionManager();
220 if (manager &&
221 (manager->RequestJSThreadExecution() == RequestState::Granted)) {
222 if (NS_IsMainThread()) {
223 // Make sure we restore permission on destruction if needed.
224 JSExecutionManager::mCurrentMTManager = manager;
226 mExecutionGrantingManager = std::move(manager);
230 AutoYieldJSThreadExecution::AutoYieldJSThreadExecution() {
231 JSExecutionManager* manager = nullptr;
233 if (NS_IsMainThread()) {
234 manager = JSExecutionManager::mCurrentMTManager;
235 } else {
236 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
238 if (workerPrivate) {
239 manager = workerPrivate->GetExecutionManager();
243 if (manager && manager->YieldJSThreadExecutionIfGranted()) {
244 mExecutionGrantingManager = std::move(manager);
245 if (NS_IsMainThread()) {
246 JSExecutionManager::mCurrentMTManager = nullptr;
251 } // namespace mozilla::dom