Bug 1392540 - Keep running debugger Promise while debugging workers. r=baku draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Thu, 10 Aug 2017 13:27:07 +0200
changeset 670956 7fca2764abd4d0a9f53dfaaff951b49f6aa0d7c0
parent 670182 e6b3498a39b94616ba36798fe0b71a3090b1b14c
child 671109 377f4ba0ea37fd725b2be294ce591c449b0ac7af
push id81779
push userbmo:poirot.alex@gmail.com
push dateWed, 27 Sep 2017 08:27:42 +0000
reviewersbaku
bugs1392540
milestone58.0a1
Bug 1392540 - Keep running debugger Promise while debugging workers. r=baku MozReview-Commit-ID: JsA0Y943egB
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/workers/WorkerPrivate.cpp
dom/workers/test/WorkerDebugger_promise_debugger.js
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -535,16 +535,32 @@ Promise::PerformMicroTaskCheckpoint()
     }
     aso.CheckForInterrupt();
     context->AfterProcessMicrotask();
   } while (!microtaskQueue.empty());
 
   return true;
 }
 
+bool
+Promise::IsWorkerDebuggerMicroTaskEmpty()
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  if (!context) {
+    return true;
+  }
+
+  std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+    &context->GetDebuggerPromiseMicroTaskQueue();
+
+  return microtaskQueue->empty();
+}
+
 void
 Promise::PerformWorkerMicroTaskCheckpoint()
 {
   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
 
   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
   if (!context) {
     return;
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -107,16 +107,17 @@ public:
 
   // Called by DOM to let us execute our callbacks.  May be called recursively.
   // Returns true if at least one microtask was processed.
   static bool PerformMicroTaskCheckpoint();
 
   static void PerformWorkerMicroTaskCheckpoint();
 
   static void PerformWorkerDebuggerMicroTaskCheckpoint();
+  static bool IsWorkerDebuggerMicroTaskEmpty();
 
   // WebIDL
 
   nsIGlobalObject* GetParentObject() const
   {
     return mGlobal;
   }
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6124,16 +6124,17 @@ WorkerPrivate::EnterDebuggerEventLoop()
   AssertIsOnWorkerThread();
 
   JSContext* cx = GetJSContext();
   MOZ_ASSERT(cx);
 
   uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
 
   while (currentEventLoopLevel <= mDebuggerEventLoopLevel) {
+
     bool debuggerRunnablesPending = false;
 
     {
       MutexAutoLock lock(mMutex);
 
       debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
     }
 
@@ -6142,25 +6143,28 @@ WorkerPrivate::EnterDebuggerEventLoop()
       SetGCTimerMode(IdleTimer);
     }
 
     // Wait for something to do
     {
       MutexAutoLock lock(mMutex);
 
       while (mControlQueue.IsEmpty() &&
-             !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty())) {
+             !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
+             Promise::IsWorkerDebuggerMicroTaskEmpty()) {
         WaitForWorkerEvents();
       }
 
       ProcessAllControlRunnablesLocked();
 
       // XXXkhuey should we abort JS on the stack here if we got Abort above?
     }
-
+    if (!Promise::IsWorkerDebuggerMicroTaskEmpty()) {
+      Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
+    }
     if (debuggerRunnablesPending) {
       // Start the periodic GC timer if it is not already running.
       SetGCTimerMode(PeriodicTimer);
 
       WorkerRunnable* runnable = nullptr;
 
       {
         MutexAutoLock lock(mMutex);
--- a/dom/workers/test/WorkerDebugger_promise_debugger.js
+++ b/dom/workers/test/WorkerDebugger_promise_debugger.js
@@ -17,14 +17,18 @@ self.onmessage = function (event) {
         }
         // This then-handler should be executed inside the nested event loop,
         // within the context of the debugger's global.
         Promise.resolve().then(function () {
           postMessage("resumed");
           leaveEventLoop();
         });
       };
-      postMessage("paused");
+      // Test bug 1392540 where DOM Promises from debugger principal
+      // where frozen while hitting a worker breakpoint.
+      Promise.resolve().then(() => {
+        postMessage("paused");
+      });
       enterEventLoop();
     };
     postMessage("resolved");
   });
 };