зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1092102 - Implement worker debugger runnables;r=khuey
This commit is contained in:
Родитель
52bea9ac64
Коммит
1b07dff8ae
|
@ -2612,6 +2612,37 @@ WorkerPrivateParent<Derived>::DispatchControlRunnable(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
nsresult
|
||||
WorkerPrivateParent<Derived>::DispatchDebuggerRunnable(
|
||||
WorkerRunnable *aDebuggerRunnable)
|
||||
{
|
||||
// May be called on any thread!
|
||||
|
||||
MOZ_ASSERT(aDebuggerRunnable);
|
||||
|
||||
nsRefPtr<WorkerRunnable> runnable = aDebuggerRunnable;
|
||||
|
||||
WorkerPrivate* self = ParentAsWorkerPrivate();
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (self->mStatus == Dead) {
|
||||
NS_WARNING("A debugger runnable was posted to a worker that is already "
|
||||
"shutting down!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Transfer ownership to the debugger queue.
|
||||
self->mDebuggerQueue.Push(runnable.forget().take());
|
||||
|
||||
mCondVar.Notify();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
already_AddRefed<WorkerRunnable>
|
||||
WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable)
|
||||
|
@ -4619,19 +4650,15 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|||
Maybe<JSAutoCompartment> workerCompartment;
|
||||
|
||||
for (;;) {
|
||||
// Workers lazily create a global object in CompileScriptRunnable. We need
|
||||
// to enter the global's compartment as soon as it has been created.
|
||||
if (!workerCompartment && GlobalScope()) {
|
||||
workerCompartment.emplace(aCx, GlobalScope()->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
Status currentStatus;
|
||||
bool debuggerRunnablesPending = false;
|
||||
bool normalRunnablesPending = false;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
while (mControlQueue.IsEmpty() &&
|
||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
||||
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
||||
WaitForWorkerEvents();
|
||||
}
|
||||
|
@ -4693,33 +4720,54 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|||
}
|
||||
}
|
||||
|
||||
// Nothing to do here if we don't have any runnables in the main queue.
|
||||
if (!normalRunnablesPending) {
|
||||
SetGCTimerMode(IdleTimer);
|
||||
continue;
|
||||
if (debuggerRunnablesPending || normalRunnablesPending) {
|
||||
// Start the periodic GC timer if it is not already running.
|
||||
SetGCTimerMode(PeriodicTimer);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_HasPendingEvents(mThread));
|
||||
if (debuggerRunnablesPending) {
|
||||
WorkerRunnable* runnable;
|
||||
|
||||
// Start the periodic GC timer if it is not already running.
|
||||
SetGCTimerMode(PeriodicTimer);
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// Process a single runnable from the main queue.
|
||||
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
|
||||
mDebuggerQueue.Pop(runnable);
|
||||
debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
|
||||
}
|
||||
|
||||
// Only perform the Promise microtask checkpoint on the outermost event
|
||||
// loop. Don't run it, for example, during sync XHR or importScripts.
|
||||
(void)Promise::PerformMicroTaskCheckpoint();
|
||||
MOZ_ASSERT(runnable);
|
||||
static_cast<nsIRunnable*>(runnable)->Run();
|
||||
runnable->Release();
|
||||
|
||||
if (NS_HasPendingEvents(mThread)) {
|
||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||
if (workerCompartment) {
|
||||
if (debuggerRunnablesPending) {
|
||||
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||
JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
|
||||
JS_MaybeGC(aCx);
|
||||
}
|
||||
} else if (normalRunnablesPending) {
|
||||
MOZ_ASSERT(NS_HasPendingEvents(mThread));
|
||||
|
||||
// Process a single runnable from the main queue.
|
||||
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
|
||||
|
||||
// Only perform the Promise microtask checkpoint on the outermost event
|
||||
// loop. Don't run it, for example, during sync XHR or importScripts.
|
||||
(void)Promise::PerformMicroTaskCheckpoint();
|
||||
|
||||
normalRunnablesPending = NS_HasPendingEvents(mThread);
|
||||
if (normalRunnablesPending && GlobalScope()) {
|
||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||
JSAutoCompartment ac(aCx, GlobalScope()->GetGlobalJSObject());
|
||||
JS_MaybeGC(aCx);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The normal event queue has been exhausted, cancel the periodic GC timer
|
||||
// and schedule the idle GC timer.
|
||||
|
||||
if (!debuggerRunnablesPending && !normalRunnablesPending) {
|
||||
// Both the debugger event queue and the normal event queue has been
|
||||
// exhausted, cancel the periodic GC timer and schedule the idle GC timer.
|
||||
SetGCTimerMode(IdleTimer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,6 +259,9 @@ public:
|
|||
nsresult
|
||||
DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable);
|
||||
|
||||
nsresult
|
||||
DispatchDebuggerRunnable(WorkerRunnable* aDebuggerRunnable);
|
||||
|
||||
already_AddRefed<WorkerRunnable>
|
||||
MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
|
@ -763,6 +766,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
|||
nsRefPtr<WorkerDebugger> mDebugger;
|
||||
|
||||
Queue<WorkerControlRunnable*, 4> mControlQueue;
|
||||
Queue<WorkerRunnable*, 4> mDebuggerQueue;
|
||||
|
||||
// Touched on multiple threads, protected with mMutex.
|
||||
JSContext* mJSContext;
|
||||
|
|
|
@ -50,6 +50,22 @@ WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
|||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerRunnable::IsDebuggerRunnable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIGlobalObject*
|
||||
WorkerRunnable::DefaultGlobalObject() const
|
||||
{
|
||||
if (IsDebuggerRunnable()) {
|
||||
return mWorkerPrivate->DebuggerGlobalScope();
|
||||
} else {
|
||||
return mWorkerPrivate->GlobalScope();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
|
@ -121,7 +137,11 @@ WorkerRunnable::DispatchInternal()
|
|||
{
|
||||
if (mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
|
||||
if (IsDebuggerRunnable()) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(this));
|
||||
} else {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
|
@ -285,9 +305,13 @@ WorkerRunnable::Run()
|
|||
MOZ_ASSERT(isMainThread == NS_IsMainThread());
|
||||
nsRefPtr<WorkerPrivate> kungFuDeathGrip;
|
||||
if (targetIsWorkerThread) {
|
||||
globalObject = mWorkerPrivate->GlobalScope();
|
||||
}
|
||||
else {
|
||||
JSObject* global = JS::CurrentGlobalOrNull(GetCurrentThreadJSContext());
|
||||
if (global) {
|
||||
globalObject = GetGlobalObjectForGlobal(global);
|
||||
} else {
|
||||
globalObject = DefaultGlobalObject();
|
||||
}
|
||||
} else {
|
||||
kungFuDeathGrip = mWorkerPrivate;
|
||||
if (isMainThread) {
|
||||
globalObject = static_cast<nsGlobalWindow*>(mWorkerPrivate->GetWindow());
|
||||
|
@ -327,8 +351,8 @@ WorkerRunnable::Run()
|
|||
|
||||
// In the case of CompileScriptRunnnable, WorkerRun above can cause us to
|
||||
// lazily create a global, so we construct aes here before calling PostRun.
|
||||
if (targetIsWorkerThread && !aes && mWorkerPrivate->GlobalScope()) {
|
||||
aes.emplace(mWorkerPrivate->GlobalScope(), false, GetCurrentThreadJSContext());
|
||||
if (targetIsWorkerThread && !aes && DefaultGlobalObject()) {
|
||||
aes.emplace(DefaultGlobalObject(), false, GetCurrentThreadJSContext());
|
||||
cx = aes->cx();
|
||||
}
|
||||
|
||||
|
@ -349,6 +373,14 @@ WorkerRunnable::Cancel()
|
|||
return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MaybeReportMainThreadException(aCx, aDispatchResult);
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
|
|
|
@ -99,6 +99,14 @@ protected:
|
|||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
|
||||
// Returns true if this runnable should be dispatched to the debugger queue,
|
||||
// and false otherwise.
|
||||
virtual bool
|
||||
IsDebuggerRunnable() const;
|
||||
|
||||
nsIGlobalObject*
|
||||
DefaultGlobalObject() const;
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also increments the busy count of |mWorkerPrivate| if targeting the
|
||||
|
@ -133,6 +141,38 @@ protected:
|
|||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
// This runnable is used to send a message to a worker debugger.
|
||||
class WorkerDebuggerRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WorkerDebuggerRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
IsDebuggerRunnable() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is used to send a message directly to a worker's sync loop.
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче