зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1527203 Part 1 - Add interface to delay execution of debuggee content in workers during debugger registration, r=asuth.
--HG-- extra : rebase_source : 7baebe68b424d99d468c06b230228e019780a123
This commit is contained in:
Родитель
899ee8e0cc
Коммит
2561d82269
|
@ -88,6 +88,17 @@ class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize performance state which might be used on the main thread, as
|
||||
// in CompileScriptRunnable. This runnable might execute first.
|
||||
aWorkerPrivate->EnsurePerformanceStorage();
|
||||
if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
|
||||
aWorkerPrivate->EnsurePerformanceCounter();
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
|
||||
|
||||
ErrorResult rv;
|
||||
|
@ -368,6 +379,12 @@ WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::SetDebuggerReady(bool aReady)
|
||||
{
|
||||
return mWorkerPrivate->SetIsDebuggerReady(aReady);
|
||||
}
|
||||
|
||||
void WorkerDebugger::Close() {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate = nullptr;
|
||||
|
|
|
@ -299,13 +299,14 @@ class ModifyBusyCountRunnable final : public WorkerControlRunnable {
|
|||
}
|
||||
};
|
||||
|
||||
class CompileScriptRunnable final : public WorkerRunnable {
|
||||
class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
|
||||
nsString mScriptURL;
|
||||
|
||||
public:
|
||||
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aScriptURL)
|
||||
: WorkerRunnable(aWorkerPrivate), mScriptURL(aScriptURL) {}
|
||||
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
|
||||
mScriptURL(aScriptURL) {}
|
||||
|
||||
private:
|
||||
// We can't implement PreRun effectively, because at the point when that would
|
||||
|
@ -1332,51 +1333,60 @@ void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
|
|||
nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget) {
|
||||
// May be called on any thread!
|
||||
MutexAutoLock lock(mMutex);
|
||||
return DispatchLockHeld(std::move(aRunnable), aSyncLoopTarget, lock);
|
||||
}
|
||||
|
||||
nsresult WorkerPrivate::DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
// May be called on any thread!
|
||||
RefPtr<WorkerRunnable> runnable(aRunnable);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
||||
|
||||
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
||||
|
||||
if (!mThread) {
|
||||
if (ParentStatus() == Pending || mStatus == Pending) {
|
||||
mPreStartRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_WARNING(
|
||||
"Using a worker event target after the thread has already"
|
||||
"been released!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
|
||||
NS_WARNING(
|
||||
"A runnable was posted to a worker that is already shutting "
|
||||
"down!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (aSyncLoopTarget) {
|
||||
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
// WorkerDebuggeeRunnables don't need any special treatment here. True,
|
||||
// they should not be delivered to a frozen worker. But frozen workers
|
||||
// aren't drawing from the thread's main event queue anyway, only from
|
||||
// mControlQueue.
|
||||
rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(),
|
||||
runnable.forget());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCondVar.Notify();
|
||||
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
|
||||
NS_WARNING(
|
||||
"A runnable was posted to a worker that is already shutting "
|
||||
"down!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
|
||||
MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
|
||||
mDelayedDebuggeeRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mThread) {
|
||||
if (ParentStatus() == Pending || mStatus == Pending) {
|
||||
mPreStartRunnables.AppendElement(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_WARNING(
|
||||
"Using a worker event target after the thread has already"
|
||||
"been released!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (aSyncLoopTarget) {
|
||||
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
// WorkerDebuggeeRunnables don't need any special treatment here. True,
|
||||
// they should not be delivered to a frozen worker. But frozen workers
|
||||
// aren't drawing from the thread's main event queue anyway, only from
|
||||
// mControlQueue.
|
||||
rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(),
|
||||
runnable.forget());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCondVar.Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2061,6 +2071,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
|
|||
mIsSecureContext(
|
||||
IsNewWorkerSecureContext(mParent, mWorkerType, mLoadInfo)),
|
||||
mDebuggerRegistered(false),
|
||||
mDebuggerReady(true),
|
||||
mIsInAutomation(false),
|
||||
mPerformanceCounter(nullptr) {
|
||||
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
||||
|
@ -2243,6 +2254,39 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
|
|||
return worker.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerPrivate::SetIsDebuggerReady(bool aReady)
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mDebuggerReady == aReady) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aReady && mDebuggerRegistered) {
|
||||
// The debugger can only be marked as not ready during registration.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mDebuggerReady = aReady;
|
||||
|
||||
if (aReady && mDebuggerRegistered) {
|
||||
// Dispatch all the delayed runnables without releasing the lock, to ensure
|
||||
// that the order in which debuggee runnables execute is the same as the
|
||||
// order in which they were originally dispatched.
|
||||
auto pending = std::move(mDelayedDebuggeeRunnables);
|
||||
for (uint32_t i = 0; i < pending.Length(); i++) {
|
||||
RefPtr<WorkerRunnable> runnable = pending[i].forget();
|
||||
nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
WorkerPrivate* aParent,
|
||||
|
|
|
@ -170,6 +170,8 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
}
|
||||
}
|
||||
|
||||
nsresult SetIsDebuggerReady(bool aReady);
|
||||
|
||||
WorkerDebugger* Debugger() const {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
|
@ -907,6 +909,12 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
data->mHolders.IsEmpty());
|
||||
}
|
||||
|
||||
// Internal logic to dispatch a runnable. This is separate from Dispatch()
|
||||
// to allow runnables to be atomically dispatched in bulk.
|
||||
nsresult DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
class EventTarget;
|
||||
friend class EventTarget;
|
||||
friend class mozilla::dom::WorkerHolder;
|
||||
|
@ -1074,6 +1082,13 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
|
||||
bool mDebuggerRegistered;
|
||||
|
||||
// During registration, this worker may be marked as not being ready to
|
||||
// execute debuggee runnables or content.
|
||||
//
|
||||
// Protected by mMutex.
|
||||
bool mDebuggerReady;
|
||||
nsTArray<RefPtr<WorkerRunnable>> mDelayedDebuggeeRunnables;
|
||||
|
||||
// mIsInAutomation is true when we're running in test automation.
|
||||
// We expose some extra testing functions in that case.
|
||||
bool mIsInAutomation;
|
||||
|
|
|
@ -47,4 +47,17 @@ interface nsIWorkerDebugger : nsISupports
|
|||
void addListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
void removeListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
// Indicate whether the debugger has finished initializing. By default the
|
||||
// debugger will be considered initialized when the onRegister hooks in all
|
||||
// nsIWorkerDebuggerManagerListener have been called.
|
||||
//
|
||||
// setDebuggerReady(false) can be called during an onRegister hook to mark
|
||||
// the debugger as not being ready yet. This will prevent all content from
|
||||
// running in the worker, including the worker's main script and any messages
|
||||
// posted to it. Other runnables will still execute in the worker as normal.
|
||||
//
|
||||
// When the debugger is ready, setDebuggerReady(true) should then be called
|
||||
// to allow the worker to begin executing content.
|
||||
void setDebuggerReady(in boolean ready);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче