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
);
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
78 workerPrivate
->SetExecutionGranted(true);
79 workerPrivate
->ScheduleTimeSliceExpiration(kTimeSliceExpirationMS
);
81 mExecutionQueue
.pop_front();
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
);
99 mExecutionQueueCondVar
.NotifyAll();
103 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
105 if (!workerPrivate
) {
109 MOZ_ASSERT(workerPrivate
->GetExecutionGranted());
111 workerPrivate
->CancelTimeSliceExpiration();
113 MutexAutoLock
lock(mExecutionQueueMutex
);
115 mExecutionQueueCondVar
.NotifyAll();
116 workerPrivate
->SetExecutionGranted(false);
119 bool JSExecutionManager::YieldJSThreadExecutionIfGranted() {
120 if (NS_IsMainThread()) {
121 if (mMainThreadIsExecuting
) {
122 YieldJSThreadExecution();
128 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
130 if (workerPrivate
&& workerPrivate
->GetExecutionGranted()) {
131 YieldJSThreadExecution();
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.
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
166 mExecutionQueueCondVar
.Wait(TimeDuration::FromMilliseconds(500));
169 mMainThreadAwaitingExecution
= false;
170 mMainThreadIsExecuting
= true;
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
;
188 mOldGrantingManager
= JSExecutionManager::mCurrentMTManager
;
190 nsPIDOMWindowInner
* innerWindow
= nullptr;
192 innerWindow
= aGlobalObject
->AsInnerWindow();
195 DocGroup
* docGroup
= nullptr;
197 docGroup
= innerWindow
->GetDocGroup();
201 manager
= docGroup
->GetExecutionManager();
204 if (JSExecutionManager::mCurrentMTManager
== manager
) {
208 if (JSExecutionManager::mCurrentMTManager
) {
209 JSExecutionManager::mCurrentMTManager
->YieldJSThreadExecution();
210 JSExecutionManager::mCurrentMTManager
= nullptr;
213 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
216 manager
= workerPrivate
->GetExecutionManager();
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
;
236 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
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