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:
Nathan Froyd 2019-01-22 20:16:56 -05:00
Родитель c7ff34b4e5
Коммит a9fb00a2f6
21 изменённых файлов: 35 добавлений и 954 удалений

Просмотреть файл

@ -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;
}