tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

JSExecutionManager.cpp (7199B)


      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/. */
      6 
      7 #include "mozilla/dom/JSExecutionManager.h"
      8 
      9 #include "WorkerCommon.h"
     10 #include "WorkerPrivate.h"
     11 #include "mozilla/StaticPrefs_dom.h"
     12 #include "mozilla/StaticPtr.h"
     13 #include "mozilla/dom/DocGroup.h"
     14 
     15 namespace mozilla::dom {
     16 
     17 JSExecutionManager* JSExecutionManager::mCurrentMTManager;
     18 
     19 const uint32_t kTimeSliceExpirationMS = 50;
     20 
     21 using RequestState = JSExecutionManager::RequestState;
     22 
     23 static StaticRefPtr<JSExecutionManager> sSABSerializationManager;
     24 
     25 void JSExecutionManager::Initialize() {
     26  if (StaticPrefs::dom_workers_serialized_sab_access()) {
     27    sSABSerializationManager = MakeRefPtr<JSExecutionManager>(1);
     28  }
     29 }
     30 
     31 void JSExecutionManager::Shutdown() { sSABSerializationManager = nullptr; }
     32 
     33 JSExecutionManager* JSExecutionManager::GetSABSerializationManager() {
     34  return sSABSerializationManager.get();
     35 }
     36 
     37 RequestState JSExecutionManager::RequestJSThreadExecution() {
     38  if (NS_IsMainThread()) {
     39    return RequestJSThreadExecutionMainThread();
     40  }
     41 
     42  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     43 
     44  if (!workerPrivate || workerPrivate->GetExecutionGranted()) {
     45    return RequestState::ExecutingAlready;
     46  }
     47 
     48  MutexAutoLock lock(mExecutionQueueMutex);
     49  MOZ_ASSERT(mMaxRunning >= mRunning);
     50 
     51  if ((mExecutionQueue.size() + (mMainThreadAwaitingExecution ? 1 : 0)) <
     52      size_t(mMaxRunning - mRunning)) {
     53    // There's slots ready for things to run, execute right away.
     54    workerPrivate->SetExecutionGranted(true);
     55    workerPrivate->ScheduleTimeSliceExpiration(kTimeSliceExpirationMS);
     56 
     57    mRunning++;
     58    return RequestState::Granted;
     59  }
     60 
     61  mExecutionQueue.push_back(workerPrivate);
     62 
     63  TimeStamp waitStart = TimeStamp::Now();
     64 
     65  while (mRunning >= mMaxRunning || (workerPrivate != mExecutionQueue.front() ||
     66                                     mMainThreadAwaitingExecution)) {
     67    // If there is no slots available, the main thread is awaiting permission
     68    // or we are not first in line for execution, wait until notified.
     69    mExecutionQueueCondVar.Wait(TimeDuration::FromMilliseconds(500));
     70    if ((TimeStamp::Now() - waitStart) > TimeDuration::FromSeconds(20)) {
     71      // Crash so that these types of situations are actually caught in the
     72      // crash reporter.
     73      MOZ_CRASH();
     74    }
     75  }
     76 
     77  workerPrivate->SetExecutionGranted(true);
     78  workerPrivate->ScheduleTimeSliceExpiration(kTimeSliceExpirationMS);
     79 
     80  mExecutionQueue.pop_front();
     81  mRunning++;
     82  if (mRunning < mMaxRunning) {
     83    // If a thread woke up before that wasn't first in line it will have gone
     84    // back to sleep, if there's more slots available, wake it now.
     85    mExecutionQueueCondVar.NotifyAll();
     86  }
     87 
     88  return RequestState::Granted;
     89 }
     90 
     91 void JSExecutionManager::YieldJSThreadExecution() {
     92  if (NS_IsMainThread()) {
     93    MOZ_ASSERT(mMainThreadIsExecuting);
     94    mMainThreadIsExecuting = false;
     95 
     96    MutexAutoLock lock(mExecutionQueueMutex);
     97    mRunning--;
     98    mExecutionQueueCondVar.NotifyAll();
     99    return;
    100  }
    101 
    102  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    103 
    104  if (!workerPrivate) {
    105    return;
    106  }
    107 
    108  MOZ_ASSERT(workerPrivate->GetExecutionGranted());
    109 
    110  workerPrivate->CancelTimeSliceExpiration();
    111 
    112  MutexAutoLock lock(mExecutionQueueMutex);
    113  mRunning--;
    114  mExecutionQueueCondVar.NotifyAll();
    115  workerPrivate->SetExecutionGranted(false);
    116 }
    117 
    118 bool JSExecutionManager::YieldJSThreadExecutionIfGranted() {
    119  if (NS_IsMainThread()) {
    120    if (mMainThreadIsExecuting) {
    121      YieldJSThreadExecution();
    122      return true;
    123    }
    124    return false;
    125  }
    126 
    127  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    128 
    129  if (workerPrivate && workerPrivate->GetExecutionGranted()) {
    130    YieldJSThreadExecution();
    131    return true;
    132  }
    133 
    134  return false;
    135 }
    136 
    137 RequestState JSExecutionManager::RequestJSThreadExecutionMainThread() {
    138  MOZ_ASSERT(NS_IsMainThread());
    139 
    140  if (mMainThreadIsExecuting) {
    141    return RequestState::ExecutingAlready;
    142  }
    143 
    144  MutexAutoLock lock(mExecutionQueueMutex);
    145  MOZ_ASSERT(mMaxRunning >= mRunning);
    146 
    147  if ((mMaxRunning - mRunning) > 0) {
    148    // If there's any slots available run, the main thread always takes
    149    // precedence over any worker threads.
    150    mRunning++;
    151    mMainThreadIsExecuting = true;
    152    return RequestState::Granted;
    153  }
    154 
    155  mMainThreadAwaitingExecution = true;
    156 
    157  TimeStamp waitStart = TimeStamp::Now();
    158 
    159  while (mRunning >= mMaxRunning) {
    160    if ((TimeStamp::Now() - waitStart) > TimeDuration::FromSeconds(20)) {
    161      // Crash so that these types of situations are actually caught in the
    162      // crash reporter.
    163      MOZ_CRASH();
    164    }
    165    mExecutionQueueCondVar.Wait(TimeDuration::FromMilliseconds(500));
    166  }
    167 
    168  mMainThreadAwaitingExecution = false;
    169  mMainThreadIsExecuting = true;
    170 
    171  mRunning++;
    172  if (mRunning < mMaxRunning) {
    173    // If a thread woke up before that wasn't first in line it will have gone
    174    // back to sleep, if there's more slots available, wake it now.
    175    mExecutionQueueCondVar.NotifyAll();
    176  }
    177 
    178  return RequestState::Granted;
    179 }
    180 
    181 AutoRequestJSThreadExecution::AutoRequestJSThreadExecution(
    182    nsIGlobalObject* aGlobalObject, bool aIsMainThread) {
    183  JSExecutionManager* manager = nullptr;
    184 
    185  mIsMainThread = aIsMainThread;
    186  if (mIsMainThread) {
    187    mOldGrantingManager = JSExecutionManager::mCurrentMTManager;
    188 
    189    nsPIDOMWindowInner* innerWindow = nullptr;
    190    if (aGlobalObject) {
    191      innerWindow = aGlobalObject->GetAsInnerWindow();
    192    }
    193 
    194    DocGroup* docGroup = nullptr;
    195    if (innerWindow) {
    196      docGroup = innerWindow->GetDocGroup();
    197    }
    198 
    199    if (docGroup) {
    200      manager = docGroup->GetExecutionManager();
    201    }
    202 
    203    if (JSExecutionManager::mCurrentMTManager == manager) {
    204      return;
    205    }
    206 
    207    if (JSExecutionManager::mCurrentMTManager) {
    208      JSExecutionManager::mCurrentMTManager->YieldJSThreadExecution();
    209      JSExecutionManager::mCurrentMTManager = nullptr;
    210    }
    211  } else {
    212    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    213 
    214    if (workerPrivate) {
    215      manager = workerPrivate->GetExecutionManager();
    216    }
    217  }
    218 
    219  if (manager &&
    220      (manager->RequestJSThreadExecution() == RequestState::Granted)) {
    221    if (NS_IsMainThread()) {
    222      // Make sure we restore permission on destruction if needed.
    223      JSExecutionManager::mCurrentMTManager = manager;
    224    }
    225    mExecutionGrantingManager = std::move(manager);
    226  }
    227 }
    228 
    229 AutoYieldJSThreadExecution::AutoYieldJSThreadExecution() {
    230  JSExecutionManager* manager = nullptr;
    231 
    232  if (NS_IsMainThread()) {
    233    manager = JSExecutionManager::mCurrentMTManager;
    234  } else {
    235    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    236 
    237    if (workerPrivate) {
    238      manager = workerPrivate->GetExecutionManager();
    239    }
    240  }
    241 
    242  if (manager && manager->YieldJSThreadExecutionIfGranted()) {
    243    mExecutionGrantingManager = std::move(manager);
    244    if (NS_IsMainThread()) {
    245      JSExecutionManager::mCurrentMTManager = nullptr;
    246    }
    247  }
    248 }
    249 
    250 }  // namespace mozilla::dom