Title: [253358] trunk/Source/_javascript_Core
Revision
253358
Author
[email protected]
Date
2019-12-10 17:45:00 -0800 (Tue, 10 Dec 2019)

Log Message

Worklist::deleteCancelledPlansForVM() should not assume that a cancelled plan is ready for deletion.
https://bugs.webkit.org/show_bug.cgi?id=205086
<rdar://problem/57795002>

Reviewed by Saam Barati.

Consider this race scenario:
1. The DFG thread finds a plan and started compiling, and it's holding a ref to
   the plan while it's compiling.
2. The GC thread discovers that we no longer need the plan and cancels it.
3. After the plan is cancelled but while the DFG thread is still compiling, the
   mutator thread calls Worklist::deleteCancelledPlansForVM().

   Worklist::deleteCancelledPlansForVM() was assuming that by the time it is
   called, Worklist::m_cancelledPlansPendingDestruction will contain the last ref
   to the cancelled plan.  However, this is an incorrect assumption, and the
   assertion there that asserts refCount == 1 will fail.

This patch fixes Worklist::deleteCancelledPlansForVM() to append the cancelled
plan back into m_cancelledPlansPendingDestruction if its refCount is not 1
(implying that the compiler thread still has a ref to it), and defer deletion of
the plan to a subsequent call to deleteCancelledPlansForVM().

This patch also adds a WTFMove to Worklist::removeDeadPlans() when we append the
cancelled plan to m_cancelledPlansPendingDestruction there.  This saves us one
unnecessary ref and deref of the plan.

* dfg/DFGWorklist.cpp:
(JSC::DFG::Worklist::deleteCancelledPlansForVM):
(JSC::DFG::Worklist::removeDeadPlans):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (253357 => 253358)


--- trunk/Source/_javascript_Core/ChangeLog	2019-12-11 01:35:14 UTC (rev 253357)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-12-11 01:45:00 UTC (rev 253358)
@@ -1,3 +1,36 @@
+2019-12-10  Mark Lam  <[email protected]>
+
+        Worklist::deleteCancelledPlansForVM() should not assume that a cancelled plan is ready for deletion.
+        https://bugs.webkit.org/show_bug.cgi?id=205086
+        <rdar://problem/57795002>
+
+        Reviewed by Saam Barati.
+
+        Consider this race scenario:
+        1. The DFG thread finds a plan and started compiling, and it's holding a ref to
+           the plan while it's compiling.
+        2. The GC thread discovers that we no longer need the plan and cancels it.
+        3. After the plan is cancelled but while the DFG thread is still compiling, the
+           mutator thread calls Worklist::deleteCancelledPlansForVM().
+
+           Worklist::deleteCancelledPlansForVM() was assuming that by the time it is
+           called, Worklist::m_cancelledPlansPendingDestruction will contain the last ref
+           to the cancelled plan.  However, this is an incorrect assumption, and the
+           assertion there that asserts refCount == 1 will fail.
+
+        This patch fixes Worklist::deleteCancelledPlansForVM() to append the cancelled
+        plan back into m_cancelledPlansPendingDestruction if its refCount is not 1
+        (implying that the compiler thread still has a ref to it), and defer deletion of
+        the plan to a subsequent call to deleteCancelledPlansForVM().
+
+        This patch also adds a WTFMove to Worklist::removeDeadPlans() when we append the
+        cancelled plan to m_cancelledPlansPendingDestruction there.  This saves us one
+        unnecessary ref and deref of the plan.
+
+        * dfg/DFGWorklist.cpp:
+        (JSC::DFG::Worklist::deleteCancelledPlansForVM):
+        (JSC::DFG::Worklist::removeDeadPlans):
+
 2019-12-10  Saam Barati  <[email protected]>
 
         methodOfGettingAValueProfileFor should return argument value profiles even when node and operandNode are the same origin

Modified: trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp (253357 => 253358)


--- trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2019-12-11 01:35:14 UTC (rev 253357)
+++ trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2019-12-11 01:45:00 UTC (rev 253358)
@@ -307,10 +307,20 @@
 void Worklist::deleteCancelledPlansForVM(LockHolder&, VM& vm)
 {
     RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
-#if !ASSERT_DISABLED
     HashSet<RefPtr<Plan>> removedPlans;
-#endif
 
+    // The following scenario can occur:
+    // 1. The DFG thread started compiling a plan.
+    // 2. The GC thread cancels the plan, and adds it to m_cancelledPlansPendingDestruction.
+    // 3. The DFG thread finishes compiling, and discovers that the thread is cancelled.
+    //    To avoid destructing the plan in the DFG thread, it adds it to
+    //    m_cancelledPlansPendingDestruction.
+    // 4. The above occurs before the mutator runs deleteCancelledPlansForVM().
+    //
+    // Hence, the same cancelled plan can appear in m_cancelledPlansPendingDestruction
+    // more than once. This is why we need to filter the cancelled plans through
+    // the removedPlans HashSet before we do the refCount check below.
+
     for (size_t i = 0; i < m_cancelledPlansPendingDestruction.size(); ++i) {
         RefPtr<Plan> plan = m_cancelledPlansPendingDestruction[i];
         if (plan->unnukedVM() != &vm)
@@ -317,18 +327,15 @@
             continue;
         m_cancelledPlansPendingDestruction[i--] = m_cancelledPlansPendingDestruction.last();
         m_cancelledPlansPendingDestruction.removeLast();
-#if !ASSERT_DISABLED
-        removedPlans.add(plan);
-#endif
+        removedPlans.add(WTFMove(plan));
     }
 
-#if !ASSERT_DISABLED
     while (!removedPlans.isEmpty()) {
         RefPtr<Plan> plan = removedPlans.takeAny();
-        RELEASE_ASSERT(plan->stage() == Plan::Cancelled);
-        RELEASE_ASSERT(plan->refCount() == 1);
+        ASSERT(plan->stage() == Plan::Cancelled);
+        if (plan->refCount() > 1)
+            m_cancelledPlansPendingDestruction.append(WTFMove(plan));
     }
-#endif
 }
 
 void Worklist::removeAllReadyPlansForVM(VM& vm, Vector<RefPtr<Plan>, 8>& myReadyPlans)
@@ -459,7 +466,7 @@
                 RefPtr<Plan> plan = m_plans.take(*iter);
                 plan->cancel();
                 if (!isInMutator)
-                    m_cancelledPlansPendingDestruction.append(plan);
+                    m_cancelledPlansPendingDestruction.append(WTFMove(plan));
             }
             Deque<RefPtr<Plan>> newQueue;
             while (!m_queue.isEmpty()) {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to