зеркало из 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;
|
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());
|
JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
|
||||||
|
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
|
@ -368,6 +379,12 @@ WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
WorkerDebugger::SetDebuggerReady(bool aReady)
|
||||||
|
{
|
||||||
|
return mWorkerPrivate->SetIsDebuggerReady(aReady);
|
||||||
|
}
|
||||||
|
|
||||||
void WorkerDebugger::Close() {
|
void WorkerDebugger::Close() {
|
||||||
MOZ_ASSERT(mWorkerPrivate);
|
MOZ_ASSERT(mWorkerPrivate);
|
||||||
mWorkerPrivate = nullptr;
|
mWorkerPrivate = nullptr;
|
||||||
|
|
|
@ -299,13 +299,14 @@ class ModifyBusyCountRunnable final : public WorkerControlRunnable {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CompileScriptRunnable final : public WorkerRunnable {
|
class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
|
||||||
nsString mScriptURL;
|
nsString mScriptURL;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
const nsAString& aScriptURL)
|
const nsAString& aScriptURL)
|
||||||
: WorkerRunnable(aWorkerPrivate), mScriptURL(aScriptURL) {}
|
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
|
||||||
|
mScriptURL(aScriptURL) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// We can't implement PreRun effectively, because at the point when that would
|
// 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,
|
nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
|
||||||
nsIEventTarget* aSyncLoopTarget) {
|
nsIEventTarget* aSyncLoopTarget) {
|
||||||
// May be called on any thread!
|
// 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);
|
RefPtr<WorkerRunnable> runnable(aRunnable);
|
||||||
|
|
||||||
{
|
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
|
|
||||||
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
|
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
|
||||||
|
NS_WARNING(
|
||||||
if (!mThread) {
|
"A runnable was posted to a worker that is already shutting "
|
||||||
if (ParentStatus() == Pending || mStatus == Pending) {
|
"down!");
|
||||||
mPreStartRunnables.AppendElement(runnable);
|
return NS_ERROR_UNEXPECTED;
|
||||||
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 (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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2061,6 +2071,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
|
||||||
mIsSecureContext(
|
mIsSecureContext(
|
||||||
IsNewWorkerSecureContext(mParent, mWorkerType, mLoadInfo)),
|
IsNewWorkerSecureContext(mParent, mWorkerType, mLoadInfo)),
|
||||||
mDebuggerRegistered(false),
|
mDebuggerRegistered(false),
|
||||||
|
mDebuggerReady(true),
|
||||||
mIsInAutomation(false),
|
mIsInAutomation(false),
|
||||||
mPerformanceCounter(nullptr) {
|
mPerformanceCounter(nullptr) {
|
||||||
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
||||||
|
@ -2243,6 +2254,39 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
|
||||||
return worker.forget();
|
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
|
// static
|
||||||
nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||||
WorkerPrivate* aParent,
|
WorkerPrivate* aParent,
|
||||||
|
|
|
@ -170,6 +170,8 @@ class WorkerPrivate : public RelativeTimeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult SetIsDebuggerReady(bool aReady);
|
||||||
|
|
||||||
WorkerDebugger* Debugger() const {
|
WorkerDebugger* Debugger() const {
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
|
@ -907,6 +909,12 @@ class WorkerPrivate : public RelativeTimeline {
|
||||||
data->mHolders.IsEmpty());
|
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;
|
class EventTarget;
|
||||||
friend class EventTarget;
|
friend class EventTarget;
|
||||||
friend class mozilla::dom::WorkerHolder;
|
friend class mozilla::dom::WorkerHolder;
|
||||||
|
@ -1074,6 +1082,13 @@ class WorkerPrivate : public RelativeTimeline {
|
||||||
|
|
||||||
bool mDebuggerRegistered;
|
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.
|
// mIsInAutomation is true when we're running in test automation.
|
||||||
// We expose some extra testing functions in that case.
|
// We expose some extra testing functions in that case.
|
||||||
bool mIsInAutomation;
|
bool mIsInAutomation;
|
||||||
|
|
|
@ -47,4 +47,17 @@ interface nsIWorkerDebugger : nsISupports
|
||||||
void addListener(in nsIWorkerDebuggerListener listener);
|
void addListener(in nsIWorkerDebuggerListener listener);
|
||||||
|
|
||||||
void removeListener(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);
|
||||||
};
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче