Title: [210398] trunk/Source
Revision
210398
Author
[email protected]
Date
2017-01-05 16:24:11 -0800 (Thu, 05 Jan 2017)

Log Message

AutomaticThread timeout shutdown leaves a small window where notify() would think that the thread is still running
https://bugs.webkit.org/show_bug.cgi?id=166742

Reviewed by Geoffrey Garen.
        
Source/_javascript_Core:

Update to new AutomaticThread API.

* dfg/DFGWorklist.cpp:

Source/WTF:

Remove the use of the RAII ThreadScope, since the use of RAII helped make this bug possible:
we'd do ~ThreadScope after we had done ~LockHolder, so in between when we decided to shut
down a thread and when it reported itself as being shut down, there was a window where a
notify() call would get confused.
        
Now, we run all thread shutdown stuff while the lock is held. We release the lock last. One
API implication is that threadWillStop becomes threadIsStopping and it's called while the
lock is held. This seems benign.

* wtf/AutomaticThread.cpp:
(WTF::AutomaticThread::start):
(WTF::AutomaticThread::threadIsStopping):
(WTF::AutomaticThread::ThreadScope::ThreadScope): Deleted.
(WTF::AutomaticThread::ThreadScope::~ThreadScope): Deleted.
(WTF::AutomaticThread::threadWillStop): Deleted.
* wtf/AutomaticThread.h:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (210397 => 210398)


--- trunk/Source/_javascript_Core/ChangeLog	2017-01-05 23:50:00 UTC (rev 210397)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-01-06 00:24:11 UTC (rev 210398)
@@ -1,3 +1,14 @@
+2017-01-05  Filip Pizlo  <[email protected]>
+
+        AutomaticThread timeout shutdown leaves a small window where notify() would think that the thread is still running
+        https://bugs.webkit.org/show_bug.cgi?id=166742
+
+        Reviewed by Geoffrey Garen.
+        
+        Update to new AutomaticThread API.
+
+        * dfg/DFGWorklist.cpp:
+
 2017-01-05  Per Arne Vollan  <[email protected]>
 
         [Win] Compile error.

Modified: trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp (210397 => 210398)


--- trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2017-01-05 23:50:00 UTC (rev 210397)
+++ trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2017-01-06 00:24:11 UTC (rev 210398)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2014, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -150,8 +150,10 @@
         m_longLivedState = std::make_unique<LongLivedState>();
     }
     
-    void threadWillStop() override
+    void threadIsStopping(const LockHolder&) override
     {
+        // We're holding the Worklist::m_lock, so we should be careful not to deadlock.
+        
         if (Options::verboseCompilationQueue())
             dataLog(m_worklist, ": Thread will stop\n");
         

Modified: trunk/Source/WTF/ChangeLog (210397 => 210398)


--- trunk/Source/WTF/ChangeLog	2017-01-05 23:50:00 UTC (rev 210397)
+++ trunk/Source/WTF/ChangeLog	2017-01-06 00:24:11 UTC (rev 210398)
@@ -1,3 +1,27 @@
+2017-01-05  Filip Pizlo  <[email protected]>
+
+        AutomaticThread timeout shutdown leaves a small window where notify() would think that the thread is still running
+        https://bugs.webkit.org/show_bug.cgi?id=166742
+
+        Reviewed by Geoffrey Garen.
+        
+        Remove the use of the RAII ThreadScope, since the use of RAII helped make this bug possible:
+        we'd do ~ThreadScope after we had done ~LockHolder, so in between when we decided to shut
+        down a thread and when it reported itself as being shut down, there was a window where a
+        notify() call would get confused.
+        
+        Now, we run all thread shutdown stuff while the lock is held. We release the lock last. One
+        API implication is that threadWillStop becomes threadIsStopping and it's called while the
+        lock is held. This seems benign.
+
+        * wtf/AutomaticThread.cpp:
+        (WTF::AutomaticThread::start):
+        (WTF::AutomaticThread::threadIsStopping):
+        (WTF::AutomaticThread::ThreadScope::ThreadScope): Deleted.
+        (WTF::AutomaticThread::ThreadScope::~ThreadScope): Deleted.
+        (WTF::AutomaticThread::threadWillStop): Deleted.
+        * wtf/AutomaticThread.h:
+
 2017-01-04  Darin Adler  <[email protected]>
 
         Remove PassRefPtr use from the "html" directory, other improvements

Modified: trunk/Source/WTF/wtf/AutomaticThread.cpp (210397 => 210398)


--- trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-01-05 23:50:00 UTC (rev 210397)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-01-06 00:24:11 UTC (rev 210398)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -147,26 +147,6 @@
         m_isRunningCondition.wait(*m_lock);
 }
 
-class AutomaticThread::ThreadScope {
-public:
-    ThreadScope(RefPtr<AutomaticThread> thread)
-        : m_thread(thread)
-    {
-        m_thread->threadDidStart();
-    }
-    
-    ~ThreadScope()
-    {
-        m_thread->threadWillStop();
-        
-        LockHolder locker(*m_thread->m_lock);
-        m_thread->m_hasUnderlyingThread = false;
-    }
-
-private:
-    RefPtr<AutomaticThread> m_thread;
-};
-
 void AutomaticThread::start(const LockHolder&)
 {
     RELEASE_ASSERT(m_isRunning);
@@ -180,18 +160,30 @@
         [=] () {
             if (verbose)
                 dataLog(RawPointer(this), ": Running automatic thread!\n");
-            ThreadScope threadScope(preserveThisForThread);
             
+            RefPtr<AutomaticThread> thread = preserveThisForThread;
+            thread->threadDidStart();
+            
             if (!ASSERT_DISABLED) {
                 LockHolder locker(*m_lock);
                 ASSERT(m_condition->contains(locker, this));
             }
             
-            auto stop = [&] (const LockHolder&) {
+            auto stopImpl = [&] (const LockHolder& locker) {
+                thread->threadIsStopping(locker);
+                thread->m_hasUnderlyingThread = false;
+            };
+            
+            auto stopPermanently = [&] (const LockHolder& locker) {
                 m_isRunning = false;
                 m_isRunningCondition.notifyAll();
+                stopImpl(locker);
             };
             
+            auto stopForTimeout = [&] (const LockHolder& locker) {
+                stopImpl(locker);
+            };
+            
             for (;;) {
                 {
                     LockHolder locker(*m_lock);
@@ -200,7 +192,7 @@
                         if (result == PollResult::Work)
                             break;
                         if (result == PollResult::Stop)
-                            return stop(locker);
+                            return stopPermanently(locker);
                         RELEASE_ASSERT(result == PollResult::Wait);
                         // Shut the thread down after one second.
                         m_isWaiting = true;
@@ -212,7 +204,10 @@
                             m_isWaiting = false;
                             if (verbose)
                                 dataLog(RawPointer(this), ": Going to sleep!\n");
-                            return;
+                            // It's important that we don't release the lock until we have completely
+                            // indicated that the thread is kaput. Otherwise we'll have a a notify
+                            // race that manifests as a deadlock on VM shutdown.
+                            return stopForTimeout(locker);
                         }
                     }
                 }
@@ -220,7 +215,7 @@
                 WorkResult result = work();
                 if (result == WorkResult::Stop) {
                     LockHolder locker(*m_lock);
-                    return stop(locker);
+                    return stopPermanently(locker);
                 }
                 RELEASE_ASSERT(result == WorkResult::Continue);
             }
@@ -232,7 +227,7 @@
 {
 }
 
-void AutomaticThread::threadWillStop()
+void AutomaticThread::threadIsStopping(const LockHolder&)
 {
 }
 

Modified: trunk/Source/WTF/wtf/AutomaticThread.h (210397 => 210398)


--- trunk/Source/WTF/wtf/AutomaticThread.h	2017-01-05 23:50:00 UTC (rev 210397)
+++ trunk/Source/WTF/wtf/AutomaticThread.h	2017-01-06 00:24:11 UTC (rev 210398)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -168,14 +168,11 @@
     // when the thread dies. These methods let you do this. You can override these methods, and you
     // can be sure that the default ones don't do anything (so you don't need a super call).
     virtual void threadDidStart();
-    virtual void threadWillStop();
+    virtual void threadIsStopping(const LockHolder&);
     
 private:
     friend class AutomaticThreadCondition;
     
-    class ThreadScope;
-    friend class ThreadScope;
-    
     void start(const LockHolder&);
     
     Box<Lock> m_lock;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to