void WorkerThread::IncrementDispatchCounter() { if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { return; } MutexAutoLock lock(mLock); if (mWorkerPrivate) { PerformanceCounter* performanceCounter = mWorkerPrivate->GetPerformanceCounter(); if (performanceCounter) { performanceCounter->IncrementDispatchCounter(DispatchCategory::Worker); } } }
void TimeoutManager::RecordExecution(Timeout* aRunningTimeout, Timeout* aTimeout) { if (!StaticPrefs::dom_performance_enable_scheduler_timing() && mWindow.IsChromeWindow()) { return; } TimeoutBudgetManager& budgetManager = TimeoutBudgetManager::Get(); TimeStamp now = TimeStamp::Now(); if (aRunningTimeout) { // If we're running a timeout callback, record any execution until // now. TimeDuration duration = budgetManager.RecordExecution( now, aRunningTimeout, mWindow.IsBackgroundInternal()); budgetManager.MaybeCollectTelemetry(now); UpdateBudget(now, duration); // This is an ad-hoc way to use the counters for the timers // that should be removed at somepoint. See Bug 1482834 PerformanceCounter* counter = GetPerformanceCounter(); if (counter) { counter->IncrementExecutionDuration(duration.ToMicroseconds()); } } if (aTimeout) { // If we're starting a new timeout callback, start recording. budgetManager.StartRecording(now); PerformanceCounter* counter = GetPerformanceCounter(); if (counter) { counter->IncrementDispatchCounter(DispatchCategory(TaskCategory::Timer)); } } else { // Else stop by clearing the start timestamp. budgetManager.StopRecording(); } }
NS_IMETHODIMP WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) { // May be called on any thread! nsCOMPtr<nsIRunnable> runnable(aRunnable); // in case we exit early // Workers only support asynchronous dispatch. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { return NS_ERROR_UNEXPECTED; } const bool onWorkerThread = PR_GetCurrentThread() == mThread; if (GetSchedulerLoggingEnabled() && onWorkerThread && mWorkerPrivate) { PerformanceCounter* performanceCounter = mWorkerPrivate->GetPerformanceCounter(); if (performanceCounter) { performanceCounter->IncrementDispatchCounter(DispatchCategory::Worker); } } #ifdef DEBUG if (runnable && !onWorkerThread) { nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable); { MutexAutoLock lock(mLock); // Only enforce cancelable runnables after we've started the worker loop. if (!mAcceptingNonWorkerRunnables) { MOZ_ASSERT(cancelable, "Only nsICancelableRunnable may be dispatched to a worker!"); } } } #endif WorkerPrivate* workerPrivate = nullptr; if (onWorkerThread) { // No need to lock here because it is only modified on this thread. MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate->AssertIsOnWorkerThread(); workerPrivate = mWorkerPrivate; } else { MutexAutoLock lock(mLock); MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX); if (mWorkerPrivate) { workerPrivate = mWorkerPrivate; // Incrementing this counter will make the worker thread sleep if it // somehow tries to unset mWorkerPrivate while we're using it. mOtherThreadsDispatchingViaEventTarget++; } } nsresult rv; if (runnable && onWorkerThread) { RefPtr<WorkerRunnable> workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL); } else { rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); } if (!onWorkerThread && workerPrivate) { // We need to wake the worker thread if we're not already on the right // thread and the dispatch succeeded. if (NS_SUCCEEDED(rv)) { MutexAutoLock workerLock(workerPrivate->mMutex); workerPrivate->mCondVar.Notify(); } // Now unset our waiting flag. { MutexAutoLock lock(mLock); MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget); if (!--mOtherThreadsDispatchingViaEventTarget) { mWorkerPrivateCondVar.Notify(); } } } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; }