зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1485216 - remove Scheduler and related code from xpcom/threads; r=mccr8
Quantum DOM is no longer a priority, and the extra code it introduces to several places block useful refactorings.
This commit is contained in:
Родитель
c7ff34b4e5
Коммит
a9fb00a2f6
|
@ -45,8 +45,6 @@ function setupServer(mm) {
|
|||
mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
|
||||
mm.removeMessageListener("debug:content-process-destroy", onDestroy);
|
||||
|
||||
Cu.unblockThreadedExecution();
|
||||
|
||||
DebuggerServer.destroy();
|
||||
gLoader.destroy();
|
||||
gLoader = null;
|
||||
|
@ -59,32 +57,23 @@ function init(msg) {
|
|||
const mm = msg.target;
|
||||
const prefix = msg.data.prefix;
|
||||
|
||||
// Using the JS debugger causes problems when we're trying to
|
||||
// schedule those zone groups across different threads. Calling
|
||||
// blockThreadedExecution causes Gecko to switch to a simpler
|
||||
// single-threaded model until unblockThreadedExecution is called
|
||||
// later. We cannot start the debugger until the callback passed to
|
||||
// blockThreadedExecution has run, signaling that we're running
|
||||
// single-threaded.
|
||||
Cu.blockThreadedExecution(() => {
|
||||
// Setup a server if none started yet
|
||||
const loader = setupServer(mm);
|
||||
// Setup a server if none started yet
|
||||
const loader = setupServer(mm);
|
||||
|
||||
// Connect both parent/child processes debugger servers RDP via message
|
||||
// managers
|
||||
const { DebuggerServer } = loader.require("devtools/server/main");
|
||||
const conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
// Connect both parent/child processes debugger servers RDP via message
|
||||
// managers
|
||||
const { DebuggerServer } = loader.require("devtools/server/main");
|
||||
const conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
|
||||
const { ContentProcessTargetActor } =
|
||||
loader.require("devtools/server/actors/targets/content-process");
|
||||
const { ActorPool } = loader.require("devtools/server/actors/common");
|
||||
const actor = new ContentProcessTargetActor(conn);
|
||||
const actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
const { ContentProcessTargetActor } =
|
||||
loader.require("devtools/server/actors/targets/content-process");
|
||||
const { ActorPool } = loader.require("devtools/server/actors/common");
|
||||
const actor = new ContentProcessTargetActor(conn);
|
||||
const actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
||||
const response = { actor: actor.form() };
|
||||
mm.sendAsyncMessage("debug:content-process-actor", response);
|
||||
});
|
||||
const response = { actor: actor.form() };
|
||||
mm.sendAsyncMessage("debug:content-process-actor", response);
|
||||
}
|
||||
|
|
|
@ -39,34 +39,25 @@ try {
|
|||
const prefix = msg.data.prefix;
|
||||
const addonId = msg.data.addonId;
|
||||
|
||||
// Using the JS debugger causes problems when we're trying to
|
||||
// schedule those zone groups across different threads. Calling
|
||||
// blockThreadedExecution causes Gecko to switch to a simpler
|
||||
// single-threaded model until unblockThreadedExecution is
|
||||
// called later. We cannot start the debugger until the callback
|
||||
// passed to blockThreadedExecution has run, signaling that
|
||||
// we're running single-threaded.
|
||||
Cu.blockThreadedExecution(() => {
|
||||
const conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
connections.set(prefix, conn);
|
||||
const conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
connections.set(prefix, conn);
|
||||
|
||||
let actor;
|
||||
let actor;
|
||||
|
||||
if (addonId) {
|
||||
const { WebExtensionTargetActor } = require("devtools/server/actors/targets/webextension");
|
||||
actor = new WebExtensionTargetActor(conn, chromeGlobal, prefix, addonId);
|
||||
} else {
|
||||
const { FrameTargetActor } = require("devtools/server/actors/targets/frame");
|
||||
actor = new FrameTargetActor(conn, chromeGlobal);
|
||||
}
|
||||
if (addonId) {
|
||||
const { WebExtensionTargetActor } = require("devtools/server/actors/targets/webextension");
|
||||
actor = new WebExtensionTargetActor(conn, chromeGlobal, prefix, addonId);
|
||||
} else {
|
||||
const { FrameTargetActor } = require("devtools/server/actors/targets/frame");
|
||||
actor = new FrameTargetActor(conn, chromeGlobal);
|
||||
}
|
||||
|
||||
const actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
const actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
||||
sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});
|
||||
});
|
||||
sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});
|
||||
});
|
||||
|
||||
addMessageListener("debug:connect", onConnect);
|
||||
|
@ -113,8 +104,6 @@ try {
|
|||
return;
|
||||
}
|
||||
|
||||
Cu.unblockThreadedExecution();
|
||||
|
||||
removeMessageListener("debug:disconnect", onDisconnect);
|
||||
// Call DebuggerServerConnection.close to destroy all child actors. It should end up
|
||||
// calling DebuggerServerConnection.onClosed that would actually cleanup all actor
|
||||
|
|
|
@ -99,7 +99,6 @@
|
|||
#include "mozilla/ProcessHangMonitorIPC.h"
|
||||
#include "mozilla/RDDProcessManager.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
@ -2187,12 +2186,6 @@ void ContentParent::LaunchSubprocessInternal(
|
|||
extraArgs.push_back("-prefMapSize");
|
||||
extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefMapSize()).get());
|
||||
|
||||
// Scheduler prefs need to be handled differently because the scheduler needs
|
||||
// to start up in the content process before the normal preferences service.
|
||||
nsPrintfCString schedulerPrefs = Scheduler::GetPrefs();
|
||||
extraArgs.push_back("-schedulerPrefs");
|
||||
extraArgs.push_back(schedulerPrefs.get());
|
||||
|
||||
if (gSafeMode) {
|
||||
extraArgs.push_back("-safeMode");
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "ContentProcess.h"
|
||||
#include "base/shared_memory.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
|
@ -81,7 +80,6 @@ static void SetUpSandboxEnvironment() {
|
|||
bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
||||
Maybe<uint64_t> childID;
|
||||
Maybe<bool> isForBrowser;
|
||||
Maybe<const char*> schedulerPrefs;
|
||||
Maybe<const char*> parentBuildID;
|
||||
char* prefsHandle = nullptr;
|
||||
char* prefMapHandle = nullptr;
|
||||
|
@ -142,12 +140,6 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
|||
return false;
|
||||
}
|
||||
prefMapSize = aArgv[i];
|
||||
} else if (strcmp(aArgv[i], "-schedulerPrefs") == 0) {
|
||||
if (++i == aArgc) {
|
||||
return false;
|
||||
}
|
||||
schedulerPrefs = Some(aArgv[i]);
|
||||
|
||||
} else if (strcmp(aArgv[i], "-safeMode") == 0) {
|
||||
gSafeMode = true;
|
||||
|
||||
|
@ -174,7 +166,7 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
|||
|
||||
// Did we find all the mandatory flags?
|
||||
if (childID.isNothing() || isForBrowser.isNothing() ||
|
||||
schedulerPrefs.isNothing() || parentBuildID.isNothing()) {
|
||||
parentBuildID.isNothing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -184,8 +176,6 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Scheduler::SetPrefs(*schedulerPrefs);
|
||||
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
recordreplay::parent::InitializeMiddleman(aArgc, aArgv, ParentPid(),
|
||||
deserializer.GetPrefsHandle(),
|
||||
|
|
|
@ -95,15 +95,6 @@ interface ScheduledGCCallback : nsISupports
|
|||
void callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for callback to be passed to Cu.blockThreadedExecution.
|
||||
*/
|
||||
[scriptable, function, uuid(c3b85a5c-c328-47d4-aaaf-384c4ff9d77d)]
|
||||
interface nsIBlockThreadedExecutionCallback : nsISupports
|
||||
{
|
||||
void callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
|
@ -672,21 +663,6 @@ interface nsIXPCComponents_Utils : nsISupports
|
|||
*/
|
||||
AUTF8String readUTF8URI(in nsIURI url);
|
||||
|
||||
/**
|
||||
* If the main thread is using any kind of fancy cooperative
|
||||
* scheduling (e.g., Quantum DOM scheduling),
|
||||
* blockThreadedExecution disables it temporarily. The
|
||||
* aBlockedCallback is called when it has been completely disabled
|
||||
* and events are back to running sequentially on a single main
|
||||
* thread. Calling unblockThreadedExecution will re-enable thread
|
||||
* scheduling of the main thread. Multiple calls to
|
||||
* blockThreadedExecution will require the same number of calls to
|
||||
* unblockThreadedExecution in order to resume cooperative
|
||||
* scheduling.
|
||||
*/
|
||||
void blockThreadedExecution(in nsIBlockThreadedExecutionCallback aBlockedCallback);
|
||||
void unblockThreadedExecution();
|
||||
|
||||
/* Give a directive to the record/replay system. */
|
||||
void recordReplayDirective(in long directive);
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "nsZipArchive.h"
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
|
@ -2405,19 +2404,6 @@ nsXPCComponents_Utils::Now(double* aRetval) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::BlockThreadedExecution(
|
||||
nsIBlockThreadedExecutionCallback* aCallback) {
|
||||
Scheduler::BlockThreadedExecution(aCallback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::UnblockThreadedExecution() {
|
||||
Scheduler::UnblockThreadedExecution();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::RecordReplayDirective(int aDirective) {
|
||||
recordreplay::RecordReplayDirective(aDirective);
|
||||
|
|
|
@ -3373,12 +3373,6 @@ pref("dom.ipc.useNativeEventProcessing.content", false);
|
|||
pref("dom.ipc.useNativeEventProcessing.content", true);
|
||||
#endif
|
||||
|
||||
// Quantum DOM scheduling:
|
||||
pref("dom.ipc.scheduler.useMultipleQueues", true);
|
||||
pref("dom.ipc.scheduler.preemption", false);
|
||||
pref("dom.ipc.scheduler.threadCount", 2);
|
||||
pref("dom.ipc.scheduler.chaoticScheduling", false);
|
||||
|
||||
// Disable support for SVG
|
||||
pref("svg.disabled", false);
|
||||
|
||||
|
|
|
@ -80,7 +80,6 @@
|
|||
|
||||
#include "mozilla/ipc/TestShellParent.h"
|
||||
#include "mozilla/ipc/XPCShellEnvironment.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/WindowsDllBlocklist.h"
|
||||
|
||||
#include "GMPProcessChild.h"
|
||||
|
@ -915,8 +914,6 @@ void XRE_ShutdownChildProcess() {
|
|||
mozilla::DebugOnly<MessageLoop*> ioLoop = XRE_GetIOMessageLoop();
|
||||
MOZ_ASSERT(!!ioLoop, "Bad shutdown order");
|
||||
|
||||
Scheduler::Shutdown();
|
||||
|
||||
// Quit() sets off the following chain of events
|
||||
// (1) UI loop starts quitting
|
||||
// (2) UI loop returns from Run() in XRE_InitChildProcess()
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include "ProfilerIOInterposeObserver.h"
|
||||
#include "mozilla/AutoProfilerLabel.h"
|
||||
#include "mozilla/ExtensionPolicyService.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/StackWalk.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
|
@ -3423,7 +3422,6 @@ ProfilingStack* profiler_register_thread(const char* aName,
|
|||
void* aGuessStackTop) {
|
||||
DEBUG_LOG("profiler_register_thread(%s)", aName);
|
||||
|
||||
MOZ_ASSERT_IF(NS_IsMainThread(), Scheduler::IsCooperativeThread());
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Make sure we have a nsThread wrapper for the current thread, and that NSPR
|
||||
|
@ -3438,8 +3436,6 @@ ProfilingStack* profiler_register_thread(const char* aName,
|
|||
}
|
||||
|
||||
void profiler_unregister_thread() {
|
||||
MOZ_ASSERT_IF(NS_IsMainThread(), Scheduler::IsCooperativeThread());
|
||||
|
||||
if (!CorePS::Exists()) {
|
||||
// This function can be called after the main thread has already shut down.
|
||||
return;
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CooperativeThreadPool.h"
|
||||
|
||||
#include "base/message_loop.h"
|
||||
#include "mozilla/IOInterposer.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "nsError.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static bool gCooperativeSchedulingEnabled;
|
||||
MOZ_THREAD_LOCAL(CooperativeThreadPool::CooperativeThread*)
|
||||
CooperativeThreadPool::sTlsCurrentThread;
|
||||
|
||||
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
|
||||
#undef Yield
|
||||
|
||||
CooperativeThreadPool::CooperativeThreadPool(size_t aNumThreads, Mutex& aMutex,
|
||||
Controller& aController)
|
||||
: mMutex(aMutex),
|
||||
mShutdownCondition(mMutex, "CoopShutdown"),
|
||||
mRunning(false),
|
||||
mNumThreads(std::min(aNumThreads, kMaxThreads)),
|
||||
mRunningThreads(0),
|
||||
mController(aController),
|
||||
mSelectedThread(size_t(0)) {
|
||||
MOZ_ASSERT(aNumThreads <= kMaxThreads);
|
||||
|
||||
gCooperativeSchedulingEnabled = true;
|
||||
sTlsCurrentThread.infallibleInit();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
mRunning = true;
|
||||
mRunningThreads = mNumThreads;
|
||||
|
||||
for (size_t i = 0; i < mNumThreads; i++) {
|
||||
mThreads[i] = MakeUnique<CooperativeThread>(this, i);
|
||||
}
|
||||
}
|
||||
|
||||
CooperativeThreadPool::~CooperativeThreadPool() { MOZ_ASSERT(!mRunning); }
|
||||
|
||||
const size_t CooperativeThreadPool::kMaxThreads;
|
||||
|
||||
void CooperativeThreadPool::Shutdown() {
|
||||
// This will not be called on any of the cooperative threads.
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mRunning);
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mNumThreads; i++) {
|
||||
mThreads[i]->BeginShutdown();
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
while (mRunningThreads) {
|
||||
mShutdownCondition.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mNumThreads; i++) {
|
||||
mThreads[i]->EndShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void CooperativeThreadPool::RecheckBlockers(const MutexAutoLock& aProofOfLock) {
|
||||
aProofOfLock.AssertOwns(mMutex);
|
||||
|
||||
if (!mSelectedThread.is<AllThreadsBlocked>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mNumThreads; i++) {
|
||||
if (mThreads[i]->mRunning && !mThreads[i]->IsBlocked(aProofOfLock)) {
|
||||
mSelectedThread = AsVariant(i);
|
||||
mThreads[i]->mCondVar.Notify();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// It may be valid to reach this point. For example, if we are waiting for an
|
||||
// event to be posted from a non-main thread. Even if the queue is non-empty,
|
||||
// it may have only idle events that we do not want to run (because we are
|
||||
// expecting a vsync soon).
|
||||
}
|
||||
|
||||
/* static */ void CooperativeThreadPool::Yield(
|
||||
Resource* aBlocker, const MutexAutoLock& aProofOfLock) {
|
||||
if (!gCooperativeSchedulingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
CooperativeThread* thread = sTlsCurrentThread.get();
|
||||
MOZ_RELEASE_ASSERT(thread);
|
||||
thread->SetBlocker(aBlocker);
|
||||
thread->Yield(aProofOfLock);
|
||||
}
|
||||
|
||||
/* static */ bool CooperativeThreadPool::IsCooperativeThread() {
|
||||
if (!gCooperativeSchedulingEnabled) {
|
||||
return false;
|
||||
}
|
||||
return !!sTlsCurrentThread.get();
|
||||
}
|
||||
|
||||
CooperativeThreadPool::SelectedThread CooperativeThreadPool::CurrentThreadIndex(
|
||||
const MutexAutoLock& aProofOfLock) const {
|
||||
aProofOfLock.AssertOwns(mMutex);
|
||||
return mSelectedThread;
|
||||
}
|
||||
|
||||
CooperativeThreadPool::CooperativeThread::CooperativeThread(
|
||||
CooperativeThreadPool* aPool, size_t aIndex)
|
||||
: mPool(aPool),
|
||||
mCondVar(aPool->mMutex, "CooperativeThreadPool"),
|
||||
mBlocker(nullptr),
|
||||
mIndex(aIndex),
|
||||
mRunning(true) {
|
||||
mThread =
|
||||
PR_CreateThread(PR_USER_THREAD, ThreadFunc, this, PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
MOZ_RELEASE_ASSERT(mThread);
|
||||
}
|
||||
|
||||
void CooperativeThreadPool::CooperativeThread::ThreadMethod() {
|
||||
char stackTop;
|
||||
|
||||
MOZ_ASSERT(gCooperativeSchedulingEnabled);
|
||||
sTlsCurrentThread.set(this);
|
||||
|
||||
nsCString name = mPool->mThreadNaming.GetNextThreadName("Main");
|
||||
PR_SetCurrentThreadName(name.get());
|
||||
|
||||
mozilla::IOInterposer::RegisterCurrentThread();
|
||||
|
||||
{
|
||||
// Make sure only one thread at a time can proceed. This only happens during
|
||||
// thread startup.
|
||||
MutexAutoLock lock(mPool->mMutex);
|
||||
while (mPool->mSelectedThread != AsVariant(mIndex)) {
|
||||
mCondVar.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
mPool->mController.OnStartThread(mIndex, name, &stackTop);
|
||||
|
||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||
mEventTarget = thread;
|
||||
|
||||
// The main event loop for this thread.
|
||||
for (;;) {
|
||||
{
|
||||
MutexAutoLock lock(mPool->mMutex);
|
||||
if (!mPool->mRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool processedEvent;
|
||||
thread->ProcessNextEvent(true, &processedEvent);
|
||||
}
|
||||
|
||||
mPool->mController.OnStopThread(mIndex);
|
||||
mozilla::IOInterposer::UnregisterCurrentThread();
|
||||
|
||||
MutexAutoLock lock(mPool->mMutex);
|
||||
mPool->mRunningThreads--;
|
||||
mRunning = false;
|
||||
mPool->mSelectedThread = AsVariant(AllThreadsBlocked::Blocked);
|
||||
mPool->RecheckBlockers(lock);
|
||||
mPool->mShutdownCondition.Notify();
|
||||
}
|
||||
|
||||
/* static */ void CooperativeThreadPool::CooperativeThread::ThreadFunc(
|
||||
void* aArg) {
|
||||
auto thread = static_cast<CooperativeThreadPool::CooperativeThread*>(aArg);
|
||||
thread->ThreadMethod();
|
||||
}
|
||||
|
||||
void CooperativeThreadPool::CooperativeThread::BeginShutdown() {
|
||||
mEventTarget->Dispatch(new mozilla::Runnable("CooperativeShutdownEvent"),
|
||||
nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void CooperativeThreadPool::CooperativeThread::EndShutdown() {
|
||||
PR_JoinThread(mThread);
|
||||
}
|
||||
|
||||
bool CooperativeThreadPool::CooperativeThread::IsBlocked(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
if (!mBlocker) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !mBlocker->IsAvailable(aProofOfLock);
|
||||
}
|
||||
|
||||
void CooperativeThreadPool::CooperativeThread::Yield(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
aProofOfLock.AssertOwns(mPool->mMutex);
|
||||
|
||||
// First select the next thread to run.
|
||||
size_t selected = mIndex + 1;
|
||||
bool found = false;
|
||||
do {
|
||||
if (selected >= mPool->mNumThreads) {
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
if (mPool->mThreads[selected]->mRunning &&
|
||||
!mPool->mThreads[selected]->IsBlocked(aProofOfLock)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
selected++;
|
||||
} while (selected != mIndex + 1);
|
||||
|
||||
if (found) {
|
||||
mPool->mSelectedThread = AsVariant(selected);
|
||||
mPool->mThreads[selected]->mCondVar.Notify();
|
||||
} else {
|
||||
// We need to block all threads. Some thread will be unblocked when
|
||||
// RecheckBlockers is called (if a new event is posted for an outside
|
||||
// thread, for example).
|
||||
mPool->mSelectedThread = AsVariant(AllThreadsBlocked::Blocked);
|
||||
}
|
||||
|
||||
mPool->mController.OnSuspendThread(mIndex);
|
||||
|
||||
while (mPool->mSelectedThread != AsVariant(mIndex)) {
|
||||
mCondVar.Wait();
|
||||
}
|
||||
|
||||
mPool->mController.OnResumeThread(mIndex);
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_CooperativeThreadPool_h
|
||||
#define mozilla_CooperativeThreadPool_h
|
||||
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prthread.h"
|
||||
|
||||
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
|
||||
#undef Yield
|
||||
|
||||
class nsIEventTarget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// A CooperativeThreadPool is a pool of threads that process jobs, with at most
|
||||
// one thread running at a given time. While processing a job, a thread can
|
||||
// yield to another thread in the pool. Threads can be blocked on abstract
|
||||
// Resources. When a thread yields, we iterate over all threads and look for
|
||||
// one whose Resource has become available. It's possible that no thread is
|
||||
// available to run, in which case all threads in the pool sleep. An outside
|
||||
// thread can get things started again by calling RecheckBlockers (presumably
|
||||
// after it has mutated some state that it expects would cause a Resource to
|
||||
// become available).
|
||||
class CooperativeThreadPool {
|
||||
public:
|
||||
// Every pool must have a controller object, on which callbacks are invoked.
|
||||
class Controller {
|
||||
public:
|
||||
// Called when a new thread in the pool is started. aIndex is the index of
|
||||
// the thread within the pool. aName is the thread name (e.g., Main#4), and
|
||||
// aStackTop is the guess for the address of the top of the stack. The
|
||||
// thread will begin processing events immediately after OnStartThread is
|
||||
// called.
|
||||
virtual void OnStartThread(size_t aIndex, const nsACString& aName,
|
||||
void* aStackTop) = 0;
|
||||
|
||||
// Called when a thread in the pool is about to be shut down.
|
||||
virtual void OnStopThread(size_t aIndex) = 0;
|
||||
|
||||
// Threads in the pool are either suspended or running. At most one thread
|
||||
// will be running at any time. All other threads will be suspended.
|
||||
|
||||
// Called when a thread is resumed (un-suspended). Note that OnResumeThread
|
||||
// will not be called if the thread is starting up. OnStartThread will be
|
||||
// called instead.
|
||||
virtual void OnResumeThread(size_t aIndex) = 0;
|
||||
|
||||
// Called when a thread in the pool is about to be suspended. Note that
|
||||
// OnSuspendThread will not be called if the thread is shutting
|
||||
// down. OnStopThread is called instead.
|
||||
virtual void OnSuspendThread(size_t aIndex) = 0;
|
||||
};
|
||||
|
||||
CooperativeThreadPool(size_t aNumThreads, Mutex& aMutex,
|
||||
Controller& aController);
|
||||
~CooperativeThreadPool();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
// An abstract class representing something that can be blocked on. Examples
|
||||
// are an event queue (where IsAvailable would check if the queue is
|
||||
// non-empty) or an object that can be owned by only one thread at a time
|
||||
// (where IsAvailable would check if the object is unowned).
|
||||
class Resource {
|
||||
public:
|
||||
virtual bool IsAvailable(const MutexAutoLock& aProofOfLock) = 0;
|
||||
};
|
||||
|
||||
// Typically called by a thread outside the pool, this will check if any
|
||||
// thread is blocked on a resource that has become available. In this case,
|
||||
// the thread will resume. Nothing is done if some thread in the pool was
|
||||
// already running.
|
||||
void RecheckBlockers(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
// Must be called from within the thread pool. Causes the current thread to be
|
||||
// blocked on aBlocker, which can be null if the thread is ready to run
|
||||
// unconditionally.
|
||||
static void Yield(Resource* aBlocker, const MutexAutoLock& aProofOfLock);
|
||||
|
||||
static bool IsCooperativeThread();
|
||||
|
||||
enum class AllThreadsBlocked { Blocked };
|
||||
using SelectedThread = Variant<size_t, AllThreadsBlocked>;
|
||||
|
||||
SelectedThread CurrentThreadIndex(const MutexAutoLock& aProofOfLock) const;
|
||||
|
||||
static const size_t kMaxThreads = 16;
|
||||
|
||||
private:
|
||||
class CooperativeThread {
|
||||
friend class CooperativeThreadPool;
|
||||
|
||||
public:
|
||||
CooperativeThread(CooperativeThreadPool* aPool, size_t aIndex);
|
||||
|
||||
void BeginShutdown();
|
||||
void EndShutdown();
|
||||
|
||||
void Started();
|
||||
|
||||
bool IsBlocked(const MutexAutoLock& aProofOfLock);
|
||||
void SetBlocker(Resource* aResource) { mBlocker = aResource; }
|
||||
|
||||
void Yield(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
private:
|
||||
static void ThreadFunc(void* aArg);
|
||||
void ThreadMethod();
|
||||
|
||||
CooperativeThreadPool* mPool;
|
||||
CondVar mCondVar;
|
||||
Resource* mBlocker;
|
||||
PRThread* mThread;
|
||||
nsCOMPtr<nsIEventTarget> mEventTarget;
|
||||
const size_t mIndex;
|
||||
bool mRunning;
|
||||
};
|
||||
|
||||
class StartRunnable;
|
||||
|
||||
Mutex& mMutex;
|
||||
CondVar mShutdownCondition;
|
||||
nsThreadPoolNaming mThreadNaming;
|
||||
|
||||
bool mRunning;
|
||||
const size_t mNumThreads;
|
||||
size_t mRunningThreads;
|
||||
Controller& mController;
|
||||
Array<UniquePtr<CooperativeThread>, kMaxThreads> mThreads;
|
||||
|
||||
SelectedThread mSelectedThread;
|
||||
|
||||
static MOZ_THREAD_LOCAL(CooperativeThread*) sTlsCurrentThread;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_CooperativeThreadPool_h
|
|
@ -1,272 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LabeledEventQueue.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
using EpochQueueEntry = SchedulerGroup::EpochQueueEntry;
|
||||
|
||||
LinkedList<SchedulerGroup>* LabeledEventQueue::sSchedulerGroups;
|
||||
size_t LabeledEventQueue::sLabeledEventQueueCount;
|
||||
SchedulerGroup* LabeledEventQueue::sCurrentSchedulerGroup;
|
||||
|
||||
LabeledEventQueue::LabeledEventQueue(EventPriority aPriority)
|
||||
: mPriority(aPriority) {
|
||||
// LabeledEventQueue should only be used by one consumer since it uses a
|
||||
// single static sSchedulerGroups field. It's hard to assert this, though, so
|
||||
// we assert NS_IsMainThread(), which is a reasonable proxy.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sLabeledEventQueueCount++ == 0) {
|
||||
sSchedulerGroups = new LinkedList<SchedulerGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
LabeledEventQueue::~LabeledEventQueue() {
|
||||
if (--sLabeledEventQueueCount == 0) {
|
||||
delete sSchedulerGroups;
|
||||
sSchedulerGroups = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static SchedulerGroup* GetSchedulerGroup(nsIRunnable* aEvent) {
|
||||
RefPtr<SchedulerGroup::Runnable> groupRunnable = do_QueryObject(aEvent);
|
||||
if (!groupRunnable) {
|
||||
// It's not labeled.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return groupRunnable->Group();
|
||||
}
|
||||
|
||||
static bool IsReadyToRun(nsIRunnable* aEvent, SchedulerGroup* aEventGroup) {
|
||||
if (!Scheduler::AnyEventRunning()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Scheduler::UnlabeledEventRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aEventGroup) {
|
||||
return !aEventGroup->IsRunning();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILabelableRunnable> labelable = do_QueryInterface(aEvent);
|
||||
if (!labelable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return labelable->IsReadyToRun();
|
||||
}
|
||||
|
||||
void LabeledEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
||||
EventPriority aPriority,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(aPriority == mPriority);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event(aEvent);
|
||||
|
||||
MOZ_ASSERT(event.get());
|
||||
|
||||
SchedulerGroup* group = GetSchedulerGroup(event);
|
||||
bool isLabeled = !!group;
|
||||
|
||||
// Create a new epoch if necessary.
|
||||
Epoch* epoch;
|
||||
if (mEpochs.IsEmpty()) {
|
||||
epoch = &mEpochs.Push(Epoch::First(isLabeled));
|
||||
} else {
|
||||
Epoch& lastEpoch = mEpochs.LastElement();
|
||||
if (lastEpoch.IsLabeled() != isLabeled) {
|
||||
epoch = &mEpochs.Push(lastEpoch.NextEpoch(isLabeled));
|
||||
} else {
|
||||
epoch = &lastEpoch;
|
||||
}
|
||||
}
|
||||
|
||||
mNumEvents++;
|
||||
epoch->mNumEvents++;
|
||||
|
||||
RunnableEpochQueue& queue =
|
||||
isLabeled ? group->GetQueue(aPriority) : mUnlabeled;
|
||||
queue.Push(EpochQueueEntry(event.forget(), epoch->mEpochNumber));
|
||||
|
||||
if (group && group->EnqueueEvent() == SchedulerGroup::NewlyQueued) {
|
||||
// This group didn't have any events before. Add it to the
|
||||
// sSchedulerGroups list.
|
||||
MOZ_ASSERT(!group->isInList());
|
||||
sSchedulerGroups->insertBack(group);
|
||||
if (!sCurrentSchedulerGroup) {
|
||||
sCurrentSchedulerGroup = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LabeledEventQueue::PopEpoch() {
|
||||
Epoch& epoch = mEpochs.FirstElement();
|
||||
MOZ_ASSERT(epoch.mNumEvents > 0);
|
||||
if (epoch.mNumEvents == 1) {
|
||||
mEpochs.Pop();
|
||||
} else {
|
||||
epoch.mNumEvents--;
|
||||
}
|
||||
|
||||
mNumEvents--;
|
||||
}
|
||||
|
||||
// Returns the next SchedulerGroup after |aGroup| in sSchedulerGroups. Wraps
|
||||
// around to the beginning of the list when we hit the end.
|
||||
/* static */ SchedulerGroup* LabeledEventQueue::NextSchedulerGroup(
|
||||
SchedulerGroup* aGroup) {
|
||||
SchedulerGroup* result = aGroup->getNext();
|
||||
if (!result) {
|
||||
result = sSchedulerGroups->getFirst();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable> LabeledEventQueue::GetEvent(
|
||||
EventPriority* aPriority, const MutexAutoLock& aProofOfLock) {
|
||||
if (mEpochs.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Epoch epoch = mEpochs.FirstElement();
|
||||
if (!epoch.IsLabeled()) {
|
||||
EpochQueueEntry& first = mUnlabeled.FirstElement();
|
||||
if (!IsReadyToRun(first.mRunnable, nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PopEpoch();
|
||||
EpochQueueEntry entry = mUnlabeled.Pop();
|
||||
MOZ_ASSERT(entry.mEpochNumber == epoch.mEpochNumber);
|
||||
MOZ_ASSERT(entry.mRunnable.get());
|
||||
return entry.mRunnable.forget();
|
||||
}
|
||||
|
||||
if (!sCurrentSchedulerGroup) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Move visible tabs to the front of the queue. The mAvoidVisibleTabCount
|
||||
// field prevents us from preferentially processing events from visible tabs
|
||||
// twice in a row. This scheme is designed to prevent starvation.
|
||||
if (TabChild::HasVisibleTabs() && mAvoidVisibleTabCount <= 0) {
|
||||
for (auto iter = TabChild::GetVisibleTabs().ConstIter(); !iter.Done();
|
||||
iter.Next()) {
|
||||
SchedulerGroup* group = iter.Get()->GetKey()->TabGroup();
|
||||
if (!group->isInList() || group == sCurrentSchedulerGroup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For each visible tab we move to the front of the queue, we have to
|
||||
// process two SchedulerGroups (the visible tab and another one,
|
||||
// presumably a background group) before we prioritize visible tabs again.
|
||||
mAvoidVisibleTabCount += 2;
|
||||
|
||||
// We move |group| right before sCurrentSchedulerGroup and then set
|
||||
// sCurrentSchedulerGroup to group.
|
||||
MOZ_ASSERT(group != sCurrentSchedulerGroup);
|
||||
group->removeFrom(*sSchedulerGroups);
|
||||
sCurrentSchedulerGroup->setPrevious(group);
|
||||
sCurrentSchedulerGroup = group;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over each SchedulerGroup once, starting at sCurrentSchedulerGroup.
|
||||
SchedulerGroup* firstGroup = sCurrentSchedulerGroup;
|
||||
SchedulerGroup* group = firstGroup;
|
||||
do {
|
||||
mAvoidVisibleTabCount--;
|
||||
|
||||
RunnableEpochQueue& queue = group->GetQueue(mPriority);
|
||||
|
||||
if (queue.IsEmpty()) {
|
||||
// This can happen if |group| is in a different LabeledEventQueue than
|
||||
// |this|.
|
||||
group = NextSchedulerGroup(group);
|
||||
continue;
|
||||
}
|
||||
|
||||
EpochQueueEntry& first = queue.FirstElement();
|
||||
if (first.mEpochNumber == epoch.mEpochNumber &&
|
||||
IsReadyToRun(first.mRunnable, group)) {
|
||||
sCurrentSchedulerGroup = NextSchedulerGroup(group);
|
||||
|
||||
PopEpoch();
|
||||
|
||||
if (group->DequeueEvent() == SchedulerGroup::NoLongerQueued) {
|
||||
// Now we can take group out of sSchedulerGroups.
|
||||
if (sCurrentSchedulerGroup == group) {
|
||||
// Since we changed sCurrentSchedulerGroup above, we'll only get here
|
||||
// if |group| was the only element in sSchedulerGroups. In that case
|
||||
// set sCurrentSchedulerGroup to null.
|
||||
MOZ_ASSERT(group->getNext() == nullptr);
|
||||
MOZ_ASSERT(group->getPrevious() == nullptr);
|
||||
sCurrentSchedulerGroup = nullptr;
|
||||
}
|
||||
group->removeFrom(*sSchedulerGroups);
|
||||
}
|
||||
EpochQueueEntry entry = queue.Pop();
|
||||
return entry.mRunnable.forget();
|
||||
}
|
||||
|
||||
group = NextSchedulerGroup(group);
|
||||
} while (group != firstGroup);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool LabeledEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) {
|
||||
return mEpochs.IsEmpty();
|
||||
}
|
||||
|
||||
size_t LabeledEventQueue::Count(const MutexAutoLock& aProofOfLock) const {
|
||||
return mNumEvents;
|
||||
}
|
||||
|
||||
bool LabeledEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) {
|
||||
if (mEpochs.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Epoch& frontEpoch = mEpochs.FirstElement();
|
||||
|
||||
if (!frontEpoch.IsLabeled()) {
|
||||
EpochQueueEntry& entry = mUnlabeled.FirstElement();
|
||||
return IsReadyToRun(entry.mRunnable, nullptr);
|
||||
}
|
||||
|
||||
// Go through the scheduler groups and look for one that has events
|
||||
// for the priority of this labeled queue that is in the current
|
||||
// epoch and is allowed to run.
|
||||
uintptr_t currentEpoch = frontEpoch.mEpochNumber;
|
||||
for (SchedulerGroup* group : *sSchedulerGroups) {
|
||||
RunnableEpochQueue& queue = group->GetQueue(mPriority);
|
||||
if (queue.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EpochQueueEntry& entry = queue.FirstElement();
|
||||
if (entry.mEpochNumber != currentEpoch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsReadyToRun(entry.mRunnable, group)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -330,5 +330,4 @@ void PrioritizedEventQueue<InnerQueueT>::ResumeInputEventPrioritization(
|
|||
|
||||
namespace mozilla {
|
||||
template class PrioritizedEventQueue<EventQueue>;
|
||||
template class PrioritizedEventQueue<LabeledEventQueue>;
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#define mozilla_PrioritizedEventQueue_h
|
||||
|
||||
#include "mozilla/AbstractEventQueue.h"
|
||||
#include "LabeledEventQueue.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
@ -145,7 +144,6 @@ class PrioritizedEventQueue final : public AbstractEventQueue {
|
|||
|
||||
class EventQueue;
|
||||
extern template class PrioritizedEventQueue<EventQueue>;
|
||||
extern template class PrioritizedEventQueue<LabeledEventQueue>;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_Scheduler_h
|
||||
#define mozilla_Scheduler_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsILabelableRunnable.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
|
||||
#undef Yield
|
||||
|
||||
class nsIBlockThreadedExecutionCallback;
|
||||
class nsIIdlePeriod;
|
||||
class nsThread;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SchedulerGroup;
|
||||
class SchedulerImpl;
|
||||
class SynchronizedEventQueue;
|
||||
|
||||
// This is the central class for scheduling work on the "main" thread. It starts
|
||||
// a pool of cooperatively scheduled threads (using CooperativeThreadPool) and
|
||||
// controls them using a single, main-thread event queue
|
||||
// (SchedulerEventQueue). Even if cooperative scheduling is not enabled,
|
||||
// Scheduler can schedule work on the main thread. Its behavior is controlled by
|
||||
// a number of preferences:
|
||||
//
|
||||
// XXX The cooperative scheduler is disabled because it will crash immediately.
|
||||
//
|
||||
// "dom.ipc.scheduler.useMultipleQueues": When this pref is true, a
|
||||
// LabeledEventQueue is used for the main thread event queue. This divides the
|
||||
// event queue into multiple queues, one per SchedulerGroup. If the pref is
|
||||
// false, a normal EventQueue is used. Either way, event prioritization via
|
||||
// PrioritizedEventQueue still happens.
|
||||
//
|
||||
// "dom.ipc.scheduler.preemption": If this pref is true, then cooperative
|
||||
// threads can be preempted before they have finished. This might happen if a
|
||||
// different cooperative thread is running an event for a higher priority
|
||||
// SchedulerGroup.
|
||||
//
|
||||
// "dom.ipc.scheduler.threadCount": The number of cooperative threads to start.
|
||||
//
|
||||
// "dom.ipc.scheduler.chaoticScheduling": When this pref is set, we make an
|
||||
// effort to switch between threads even when it is not necessary to do
|
||||
// this. This is useful for debugging.
|
||||
class Scheduler {
|
||||
public:
|
||||
static already_AddRefed<nsThread> Init(nsIIdlePeriod* aIdlePeriod);
|
||||
static void Start();
|
||||
static void Shutdown();
|
||||
|
||||
// Scheduler prefs need to be handled differently because the scheduler needs
|
||||
// to start up in the content process before the normal preferences service.
|
||||
static nsPrintfCString GetPrefs();
|
||||
static void SetPrefs(const char* aPrefs);
|
||||
|
||||
static bool IsSchedulerEnabled();
|
||||
static bool UseMultipleQueues();
|
||||
|
||||
static bool IsCooperativeThread();
|
||||
|
||||
static void Yield();
|
||||
|
||||
static bool UnlabeledEventRunning();
|
||||
static bool AnyEventRunning();
|
||||
|
||||
static void BlockThreadedExecution(
|
||||
nsIBlockThreadedExecutionCallback* aCallback);
|
||||
static void UnblockThreadedExecution();
|
||||
|
||||
class MOZ_RAII EventLoopActivation {
|
||||
public:
|
||||
using EventGroups = nsILabelableRunnable::SchedulerGroupSet;
|
||||
EventLoopActivation();
|
||||
~EventLoopActivation();
|
||||
|
||||
static void Init();
|
||||
|
||||
bool IsNested() const { return !!mPrev; }
|
||||
|
||||
void SetEvent(nsIRunnable* aEvent, EventPriority aPriority);
|
||||
|
||||
EventPriority Priority() const { return mPriority; }
|
||||
bool IsLabeled() { return mIsLabeled; }
|
||||
EventGroups& EventGroupsAffected() { return mEventGroups; }
|
||||
|
||||
private:
|
||||
EventLoopActivation* mPrev;
|
||||
bool mProcessingEvent;
|
||||
bool mIsLabeled;
|
||||
EventGroups mEventGroups;
|
||||
EventPriority mPriority;
|
||||
|
||||
static MOZ_THREAD_LOCAL(EventLoopActivation*) sTopActivation;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class SchedulerImpl;
|
||||
static UniquePtr<SchedulerImpl> sScheduler;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_Scheduler_h
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "mozilla/ThreadEventQueue.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "LabeledEventQueue.h"
|
||||
|
||||
#include "LeakRefPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
@ -290,5 +289,4 @@ void ThreadEventQueue<InnerQueueT>::SetObserver(nsIThreadObserver* aObserver) {
|
|||
namespace mozilla {
|
||||
template class ThreadEventQueue<EventQueue>;
|
||||
template class ThreadEventQueue<PrioritizedEventQueue<EventQueue>>;
|
||||
template class ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>;
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -24,8 +24,6 @@ class EventQueue;
|
|||
template <typename InnerQueueT>
|
||||
class PrioritizedEventQueue;
|
||||
|
||||
class LabeledEventQueue;
|
||||
|
||||
class ThreadEventTarget;
|
||||
|
||||
// A ThreadEventQueue implements normal monitor-style synchronization over the
|
||||
|
@ -116,8 +114,6 @@ class ThreadEventQueue final : public SynchronizedEventQueue {
|
|||
|
||||
extern template class ThreadEventQueue<EventQueue>;
|
||||
extern template class ThreadEventQueue<PrioritizedEventQueue<EventQueue>>;
|
||||
extern template class ThreadEventQueue<
|
||||
PrioritizedEventQueue<LabeledEventQueue>>;
|
||||
|
||||
}; // namespace mozilla
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ EXPORTS.mozilla += [
|
|||
'AbstractThread.h',
|
||||
'BlockingResourceBase.h',
|
||||
'CondVar.h',
|
||||
'CooperativeThreadPool.h',
|
||||
'CPUUsageWatcher.h',
|
||||
'DataMutex.h',
|
||||
'DeadlockDetector.h',
|
||||
|
@ -57,7 +56,6 @@ EXPORTS.mozilla += [
|
|||
'RecursiveMutex.h',
|
||||
'ReentrantMonitor.h',
|
||||
'RWLock.h',
|
||||
'Scheduler.h',
|
||||
'SchedulerGroup.h',
|
||||
'SharedThreadPool.h',
|
||||
'StateMirroring.h',
|
||||
|
@ -81,11 +79,9 @@ SOURCES += [
|
|||
UNIFIED_SOURCES += [
|
||||
'AbstractThread.cpp',
|
||||
'BlockingResourceBase.cpp',
|
||||
'CooperativeThreadPool.cpp',
|
||||
'CPUUsageWatcher.cpp',
|
||||
'EventQueue.cpp',
|
||||
'InputEventStatistics.cpp',
|
||||
'LabeledEventQueue.cpp',
|
||||
'LazyIdleThread.cpp',
|
||||
'MainThreadIdlePeriod.cpp',
|
||||
'nsEnvironment.cpp',
|
||||
|
@ -102,7 +98,6 @@ UNIFIED_SOURCES += [
|
|||
'PrioritizedEventQueue.cpp',
|
||||
'RecursiveMutex.cpp',
|
||||
'RWLock.cpp',
|
||||
'Scheduler.cpp',
|
||||
'SchedulerGroup.cpp',
|
||||
'SharedThreadPool.cpp',
|
||||
'SynchronizedEventQueue.cpp',
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
|
||||
#include "nsILabelableRunnable.h"
|
||||
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
|
||||
bool nsILabelableRunnable::IsReadyToRun() {
|
||||
MOZ_ASSERT(mozilla::Scheduler::AnyEventRunning());
|
||||
MOZ_ASSERT(!mozilla::Scheduler::UnlabeledEventRunning());
|
||||
SchedulerGroupSet groups;
|
||||
if (!GetAffectedSchedulerGroups(groups)) {
|
||||
// it can not be labeled right now.
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "mozilla/ipc/MessageChannel.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
|
@ -1052,10 +1051,8 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
// yet.
|
||||
bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
|
||||
|
||||
Maybe<Scheduler::EventLoopActivation> activation;
|
||||
if (IsMainThread()) {
|
||||
DoMainThreadSpecificProcessing(reallyWait);
|
||||
activation.emplace();
|
||||
}
|
||||
|
||||
++mNestedEventLoopDepth;
|
||||
|
@ -1090,10 +1087,6 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
EventPriority priority;
|
||||
nsCOMPtr<nsIRunnable> event = mEvents->GetEvent(reallyWait, &priority);
|
||||
|
||||
if (activation.isSome()) {
|
||||
activation.ref().SetEvent(event, priority);
|
||||
}
|
||||
|
||||
*aResult = (event.get() != nullptr);
|
||||
|
||||
if (event) {
|
||||
|
|
|
@ -11,13 +11,11 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "LabeledEventQueue.h"
|
||||
#include "MainThreadQueue.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/ThreadEventQueue.h"
|
||||
|
@ -49,16 +47,12 @@ void NS_SetMainThread() {
|
|||
}
|
||||
|
||||
void NS_SetMainThread(PRThread* aVirtualThread) {
|
||||
MOZ_ASSERT(Scheduler::IsCooperativeThread());
|
||||
|
||||
MOZ_ASSERT(!gTlsCurrentVirtualThread.get());
|
||||
gTlsCurrentVirtualThread.set(aVirtualThread);
|
||||
NS_SetMainThread();
|
||||
}
|
||||
|
||||
void NS_UnsetMainThread() {
|
||||
MOZ_ASSERT(Scheduler::IsCooperativeThread());
|
||||
|
||||
sTLSIsMainThread.set(false);
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
gTlsCurrentVirtualThread.set(nullptr);
|
||||
|
@ -206,8 +200,6 @@ nsresult nsThreadManager::Init() {
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
Scheduler::EventLoopActivation::Init();
|
||||
|
||||
if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -224,21 +216,9 @@ nsresult nsThreadManager::Init() {
|
|||
|
||||
nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod();
|
||||
|
||||
bool startScheduler = false;
|
||||
if (XRE_IsContentProcess() && Scheduler::IsSchedulerEnabled()) {
|
||||
mMainThread = Scheduler::Init(idlePeriod);
|
||||
startScheduler = true;
|
||||
} else {
|
||||
if (XRE_IsContentProcess() && Scheduler::UseMultipleQueues()) {
|
||||
mMainThread = CreateMainThread<
|
||||
ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>,
|
||||
LabeledEventQueue>(idlePeriod);
|
||||
} else {
|
||||
mMainThread =
|
||||
CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<EventQueue>>,
|
||||
EventQueue>(idlePeriod);
|
||||
}
|
||||
}
|
||||
mMainThread =
|
||||
CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<EventQueue>>,
|
||||
EventQueue>(idlePeriod);
|
||||
|
||||
nsresult rv = mMainThread->InitCurrentThread();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -256,9 +236,6 @@ nsresult nsThreadManager::Init() {
|
|||
|
||||
mInitialized = true;
|
||||
|
||||
if (startScheduler) {
|
||||
Scheduler::Start();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче