Title: [264673] trunk/Source/_javascript_Core
Revision
264673
Author
keith_mil...@apple.com
Date
2020-07-21 12:36:56 -0700 (Tue, 21 Jul 2020)

Log Message

Fix FinalizationRegistry GC finalizer interation
https://bugs.webkit.org/show_bug.cgi?id=214586
<rdar://65854264>

Reviewed by Mark Lam and Yusuke Suzuki.

Turns out when you remove the ith element from a Vector and you
increment the index anyway you skip things...  Use the helper
functions instead. This fixes an ASAN crash on our
FinalizationRegistry tests.

* heap/Heap.cpp:
(JSC::Heap::finalizeUnconditionalFinalizers):
* runtime/DeferredWorkTimer.cpp:
(JSC::DeferredWorkTimer::addPendingWork):
(JSC::DeferredWorkTimer::hasPendingWork):
(JSC::DeferredWorkTimer::hasDependancyInPendingWork):
(JSC::DeferredWorkTimer::cancelPendingWork):
* runtime/JSFinalizationRegistry.cpp:
(JSC::JSFinalizationRegistry::finalizeUnconditionally):
(JSC::JSFinalizationRegistry::takeDeadHoldingsValue):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (264672 => 264673)


--- trunk/Source/_javascript_Core/ChangeLog	2020-07-21 19:29:00 UTC (rev 264672)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-07-21 19:36:56 UTC (rev 264673)
@@ -1,3 +1,27 @@
+2020-07-21  Keith Miller  <keith_mil...@apple.com>
+
+        Fix FinalizationRegistry GC finalizer interation
+        https://bugs.webkit.org/show_bug.cgi?id=214586
+        <rdar://65854264>
+
+        Reviewed by Mark Lam and Yusuke Suzuki.
+
+        Turns out when you remove the ith element from a Vector and you
+        increment the index anyway you skip things...  Use the helper
+        functions instead. This fixes an ASAN crash on our
+        FinalizationRegistry tests.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::finalizeUnconditionalFinalizers):
+        * runtime/DeferredWorkTimer.cpp:
+        (JSC::DeferredWorkTimer::addPendingWork):
+        (JSC::DeferredWorkTimer::hasPendingWork):
+        (JSC::DeferredWorkTimer::hasDependancyInPendingWork):
+        (JSC::DeferredWorkTimer::cancelPendingWork):
+        * runtime/JSFinalizationRegistry.cpp:
+        (JSC::JSFinalizationRegistry::finalizeUnconditionally):
+        (JSC::JSFinalizationRegistry::takeDeadHoldingsValue):
+
 2020-07-21  Saam Barati  <sbar...@apple.com>
 
         Disable NO_SMT by default

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (264672 => 264673)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2020-07-21 19:29:00 UTC (rev 264672)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2020-07-21 19:36:56 UTC (rev 264673)
@@ -612,6 +612,8 @@
         finalizeMarkedUnconditionalFinalizers<JSWeakObjectRef>(*vm().m_weakObjectRefSpace);
     if (vm().m_errorInstanceSpace)
         finalizeMarkedUnconditionalFinalizers<ErrorInstance>(*vm().m_errorInstanceSpace);
+
+    // FinalizationRegistries currently rely on serial finalization because they can post tasks to the deferredWorkTimer, which normally expects tasks to only be posted by the API lock holder.
     if (vm().m_finalizationRegistrySpace)
         finalizeMarkedUnconditionalFinalizers<JSFinalizationRegistry>(*vm().m_finalizationRegistrySpace);
 

Modified: trunk/Source/_javascript_Core/runtime/DeferredWorkTimer.cpp (264672 => 264673)


--- trunk/Source/_javascript_Core/runtime/DeferredWorkTimer.cpp	2020-07-21 19:29:00 UTC (rev 264672)
+++ trunk/Source/_javascript_Core/runtime/DeferredWorkTimer.cpp	2020-07-21 19:36:56 UTC (rev 264673)
@@ -110,7 +110,7 @@
 
 void DeferredWorkTimer::addPendingWork(VM& vm, Ticket ticket, Vector<Strong<JSCell>>&& dependencies)
 {
-    ASSERT(vm.currentThreadIsHoldingAPILock());
+    ASSERT(vm.currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && vm.heap.worldIsStopped()));
     for (unsigned i = 0; i < dependencies.size(); ++i)
         ASSERT(dependencies[i].get() != ticket);
 
@@ -127,13 +127,13 @@
 
 bool DeferredWorkTimer::hasPendingWork(Ticket ticket)
 {
-    ASSERT(ticket->vm().currentThreadIsHoldingAPILock());
+    ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
     return m_pendingTickets.contains(ticket);
 }
 
 bool DeferredWorkTimer::hasDependancyInPendingWork(Ticket ticket, JSCell* dependency)
 {
-    ASSERT(ticket->vm().currentThreadIsHoldingAPILock());
+    ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
     ASSERT(m_pendingTickets.contains(ticket));
 
     auto result = m_pendingTickets.get(ticket);
@@ -142,7 +142,7 @@
 
 bool DeferredWorkTimer::cancelPendingWork(Ticket ticket)
 {
-    ASSERT(ticket->vm().currentThreadIsHoldingAPILock());
+    ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
     bool result = m_pendingTickets.remove(ticket);
 
     if (result)

Modified: trunk/Source/_javascript_Core/runtime/JSFinalizationRegistry.cpp (264672 => 264673)


--- trunk/Source/_javascript_Core/runtime/JSFinalizationRegistry.cpp	2020-07-21 19:29:00 UTC (rev 264672)
+++ trunk/Source/_javascript_Core/runtime/JSFinalizationRegistry.cpp	2020-07-21 19:36:56 UTC (rev 264673)
@@ -92,39 +92,52 @@
 {
     auto locker = holdLock(cellLock());
 
-    auto removeFromVector = [] (auto& vector, size_t position) {
-        std::swap(vector[position], vector.last());
-        return vector.takeLast();
-    };
+#if ASSERT_ENABLED
+    for (const auto& iter : m_deadRegistrations)
+        RELEASE_ASSERT(iter.value.size());
+#endif
 
     bool readiedCell = false;
-    for (unsigned i = 0; i < m_noUnregistrationLive.size(); ++i) {
-        ASSERT(!m_noUnregistrationLive[i].holdings.get().isCell() || vm.heap.isMarked(m_noUnregistrationLive[i].holdings.get().asCell()));
-        if (!vm.heap.isMarked(m_noUnregistrationLive[i].target)) {
-            m_noUnregistrationDead.append(removeFromVector(m_noUnregistrationLive, i).holdings);
+    m_noUnregistrationLive.removeAllMatching([&] (const Registration& reg) {
+        ASSERT(!reg.holdings.get().isCell() || vm.heap.isMarked(reg.holdings.get().asCell()));
+        if (!vm.heap.isMarked(reg.target)) {
+            m_noUnregistrationDead.append(reg.holdings);
             readiedCell = true;
+            return true;
         }
-    }
+        return false;
+    });
 
     m_liveRegistrations.removeIf([&] (auto& bucket) -> bool {
         ASSERT(bucket.value.size());
 
         bool keyIsDead = !vm.heap.isMarked(bucket.key);
+        DeadRegistrations* deadList = nullptr;
+        auto getDeadList = [&] () -> DeadRegistrations& {
+            if (UNLIKELY(!deadList))
+                deadList = &m_deadRegistrations.add(bucket.key, DeadRegistrations()).iterator->value;
+            return *deadList;
+        };
 
-        for (size_t i = 0; i < bucket.value.size(); i++) {
-            ASSERT(!bucket.value[i].holdings.get().isCell() || vm.heap.isMarked(bucket.value[i].holdings.get().asCell()));
-            if (!vm.heap.isMarked(bucket.value[i].target)) {
+        bucket.value.removeAllMatching([&] (const Registration& reg) {
+            ASSERT(!reg.holdings.get().isCell() || vm.heap.isMarked(reg.holdings.get().asCell()));
+            if (!vm.heap.isMarked(reg.target)) {
                 if (keyIsDead)
-                    m_noUnregistrationDead.append(removeFromVector(bucket.value, i).holdings);
-                else {
-                    auto deadList = m_deadRegistrations.add(bucket.key, DeadRegistrations());
-                    deadList.iterator->value.append(removeFromVector(bucket.value, i).holdings);
-                }
+                    m_noUnregistrationDead.append(reg.holdings);
+                else
+                    getDeadList().append(reg.holdings);
                 readiedCell = true;
-            } else if (keyIsDead)
-                m_noUnregistrationLive.append(removeFromVector(bucket.value, i));
-        }
+                return true;
+            }
 
+            if (keyIsDead) {
+                m_noUnregistrationLive.append(reg);
+                return true;
+            }
+
+            return false;
+        });
+
         return !bucket.value.size();
     });
 
@@ -169,10 +182,10 @@
             m_deadRegistrations.remove(iter);
     }
 
-    if constexpr (ASSERT_ENABLED) {
-        for (const auto& iter : m_deadRegistrations)
-            RELEASE_ASSERT(iter.value.size());
-    }
+#if ASSERT_ENABLED
+    for (const auto& iter : m_deadRegistrations)
+        RELEASE_ASSERT(iter.value.size());
+#endif
 
     return result;
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to