Title: [260682] trunk/Source
Revision
260682
Author
[email protected]
Date
2020-04-24 16:56:38 -0700 (Fri, 24 Apr 2020)

Log Message

[WTF] allThreads registration is racy with allThreads unregistration
https://bugs.webkit.org/show_bug.cgi?id=210995
<rdar://problem/61609690>

Reviewed by Keith Miller.

Source/WebCore:

* page/cocoa/ResourceUsageThreadCocoa.mm:
(WebCore::ResourceUsageThread::platformCollectCPUData):

Source/WTF:

There is a race between registering a thread to allThreads and unregistering a thread from allThreads.

    1. Caller: A new thread is created, but not registering it to allThreads yet.
    2. Thread: The thread is running.
    3. Thread: The thread finishes its execution before the thread is registered into allThreads.
    4. Thread: The thread unregisters itself from allThreads.
    5. Caller: Registers the new thread to allThreads after it already finished its execution.
    6. The thread is never removed from allThreads.

This patch adds m_didUnregisterFromAllThreads flag to Thread, and add the thread to allThreads only when this flag is false.

Covered by LayoutTests/inspector/cpu-profiler/threads.html.

* wtf/Threading.cpp:
(WTF::Thread::create):
(WTF::Thread::didExit):
* wtf/Threading.h:
(WTF::Thread::Thread):

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (260681 => 260682)


--- trunk/Source/WTF/ChangeLog	2020-04-24 23:53:34 UTC (rev 260681)
+++ trunk/Source/WTF/ChangeLog	2020-04-24 23:56:38 UTC (rev 260682)
@@ -1,3 +1,30 @@
+2020-04-24  Yusuke Suzuki  <[email protected]>
+
+        [WTF] allThreads registration is racy with allThreads unregistration
+        https://bugs.webkit.org/show_bug.cgi?id=210995
+        <rdar://problem/61609690>
+
+        Reviewed by Keith Miller.
+
+        There is a race between registering a thread to allThreads and unregistering a thread from allThreads.
+
+            1. Caller: A new thread is created, but not registering it to allThreads yet.
+            2. Thread: The thread is running.
+            3. Thread: The thread finishes its execution before the thread is registered into allThreads.
+            4. Thread: The thread unregisters itself from allThreads.
+            5. Caller: Registers the new thread to allThreads after it already finished its execution.
+            6. The thread is never removed from allThreads.
+
+        This patch adds m_didUnregisterFromAllThreads flag to Thread, and add the thread to allThreads only when this flag is false.
+
+        Covered by LayoutTests/inspector/cpu-profiler/threads.html.
+
+        * wtf/Threading.cpp:
+        (WTF::Thread::create):
+        (WTF::Thread::didExit):
+        * wtf/Threading.h:
+        (WTF::Thread::Thread):
+
 2020-04-24  Alex Christensen  <[email protected]>
 
         REGRESSION(260485) Payment requests don't do anything

Modified: trunk/Source/WTF/wtf/Threading.cpp (260681 => 260682)


--- trunk/Source/WTF/wtf/Threading.cpp	2020-04-24 23:53:34 UTC (rev 260681)
+++ trunk/Source/WTF/wtf/Threading.cpp	2020-04-24 23:56:38 UTC (rev 260682)
@@ -196,9 +196,14 @@
 #endif
     }
 
+    // We must register threads here since threads registered in allThreads are expected to have complete thread data which can be initialized in launched thread side.
+    // However, it is also possible that the launched thread has finished its execution before it is registered in allThreads here! In this case, the thread has already
+    // called Thread::didExit to unregister itself from allThreads. Registering such a thread will register a stale thread pointer to allThreads, which will not be removed
+    // even after Thread is destroyed. Register a thread only when it has not unregistered itself from allThreads yet.
     {
-        LockHolder lock(allThreadsMutex());
-        allThreads(lock).add(&thread.get());
+        auto locker = holdLock(allThreadsMutex());
+        if (!thread->m_didUnregisterFromAllThreads)
+            allThreads(locker).add(thread.ptr());
     }
 
     ASSERT(!thread->stack().isEmpty());
@@ -222,8 +227,9 @@
 void Thread::didExit()
 {
     {
-        LockHolder lock(allThreadsMutex());
-        allThreads(lock).remove(this);
+        auto locker = holdLock(allThreadsMutex());
+        allThreads(locker).remove(this);
+        m_didUnregisterFromAllThreads = true;
     }
 
     if (shouldRemoveThreadFromThreadGroup()) {

Modified: trunk/Source/WTF/wtf/Threading.h (260681 => 260682)


--- trunk/Source/WTF/wtf/Threading.h	2020-04-24 23:53:34 UTC (rev 260681)
+++ trunk/Source/WTF/wtf/Threading.h	2020-04-24 23:56:38 UTC (rev 260682)
@@ -316,6 +316,8 @@
     bool m_isCompilationThread: 1;
     unsigned m_gcThreadType : 2;
 
+    bool m_didUnregisterFromAllThreads { false };
+
     // Lock & ParkingLot rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed.
     // Use WordLock since WordLock does not depend on ThreadSpecific and this "Thread".
     WordLock m_mutex;

Modified: trunk/Source/WebCore/ChangeLog (260681 => 260682)


--- trunk/Source/WebCore/ChangeLog	2020-04-24 23:53:34 UTC (rev 260681)
+++ trunk/Source/WebCore/ChangeLog	2020-04-24 23:56:38 UTC (rev 260682)
@@ -1,3 +1,14 @@
+2020-04-24  Yusuke Suzuki  <[email protected]>
+
+        [WTF] allThreads registration is racy with allThreads unregistration
+        https://bugs.webkit.org/show_bug.cgi?id=210995
+        <rdar://problem/61609690>
+
+        Reviewed by Keith Miller.
+
+        * page/cocoa/ResourceUsageThreadCocoa.mm:
+        (WebCore::ResourceUsageThread::platformCollectCPUData):
+
 2020-04-24  Wenson Hsieh  <[email protected]>
 
         Make some more adjustments to TextManipulationController's paragraph boundary heuristic

Modified: trunk/Source/WebCore/page/cocoa/ResourceUsageThreadCocoa.mm (260681 => 260682)


--- trunk/Source/WebCore/page/cocoa/ResourceUsageThreadCocoa.mm	2020-04-24 23:53:34 UTC (rev 260681)
+++ trunk/Source/WebCore/page/cocoa/ResourceUsageThreadCocoa.mm	2020-04-24 23:56:38 UTC (rev 260682)
@@ -156,8 +156,8 @@
 
     HashSet<mach_port_t> knownWebKitThreads;
     {
-        LockHolder lock(Thread::allThreadsMutex());
-        for (auto* thread : Thread::allThreads(lock)) {
+        auto locker = holdLock(Thread::allThreadsMutex());
+        for (auto* thread : Thread::allThreads(locker)) {
             mach_port_t machThread = thread->machThread();
             if (machThread != MACH_PORT_NULL)
                 knownWebKitThreads.add(machThread);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to