Title: [207545] trunk/Source
Revision
207545
Author
fpi...@apple.com
Date
2016-10-19 10:47:30 -0700 (Wed, 19 Oct 2016)

Log Message

DFG worklist should use AutomaticThread
https://bugs.webkit.org/show_bug.cgi?id=163615

Reviewed by Mark Lam.
        
Source/_javascript_Core:

AutomaticThread is a new feature in WTF that allows you to easily create worker threads that
shut down automatically. This changes DFG::Worklist to use AutomaticThread, so that its
threads shut down automatically, too. This has the potential to save a lot of memory.
        
This required some improvements to AutomaticThread: Worklist likes to be able to keep state
around for the whole lifetime of a thread, and so it likes knowing when threads are born and
when they die. I added virtual methods for that. Also, Worklist uses notifyOne() so I added
that, too.
        
This looks to be perf-neutral.

* dfg/DFGThreadData.cpp:
(JSC::DFG::ThreadData::ThreadData):
* dfg/DFGThreadData.h:
* dfg/DFGWorklist.cpp:
(JSC::DFG::Worklist::ThreadBody::ThreadBody):
(JSC::DFG::Worklist::Worklist):
(JSC::DFG::Worklist::~Worklist):
(JSC::DFG::Worklist::finishCreation):
(JSC::DFG::Worklist::isActiveForVM):
(JSC::DFG::Worklist::enqueue):
(JSC::DFG::Worklist::compilationState):
(JSC::DFG::Worklist::waitUntilAllPlansForVMAreReady):
(JSC::DFG::Worklist::removeAllReadyPlansForVM):
(JSC::DFG::Worklist::completeAllReadyPlansForVM):
(JSC::DFG::Worklist::rememberCodeBlocks):
(JSC::DFG::Worklist::visitWeakReferences):
(JSC::DFG::Worklist::removeDeadPlans):
(JSC::DFG::Worklist::removeNonCompilingPlansForVM):
(JSC::DFG::Worklist::queueLength):
(JSC::DFG::Worklist::dump):
(JSC::DFG::Worklist::runThread): Deleted.
(JSC::DFG::Worklist::threadFunction): Deleted.
* dfg/DFGWorklist.h:

Source/WTF:

This adds new functionality to AutomaticThread to support DFG::Worklist:
        
- AutomaticThread::threadDidStart/threadWillStop virtual methods called at the start and end
  of a thread's lifetime. This allows Worklist to tie some resources to the life of the
  thread, and also means that now those resources will naturally free up when the Worklist is
  not in use.
        
- AutomaticThreadCondition::notifyOne(). This required changes to Condition::notifyOne(). We
  need to know if the Condition woke up anyone. If it didn't, then we need to launch one of
  our threads.

* wtf/AutomaticThread.cpp:
(WTF::AutomaticThreadCondition::notifyOne):
(WTF::AutomaticThread::ThreadScope::ThreadScope):
(WTF::AutomaticThread::ThreadScope::~ThreadScope):
(WTF::AutomaticThread::start):
(WTF::AutomaticThread::threadDidStart):
(WTF::AutomaticThread::threadWillStop):
* wtf/AutomaticThread.h:
* wtf/Condition.h:
(WTF::ConditionBase::notifyOne):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (207544 => 207545)


--- trunk/Source/_javascript_Core/ChangeLog	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-10-19 17:47:30 UTC (rev 207545)
@@ -1,3 +1,45 @@
+2016-10-18  Filip Pizlo  <fpi...@apple.com>
+
+        DFG worklist should use AutomaticThread
+        https://bugs.webkit.org/show_bug.cgi?id=163615
+
+        Reviewed by Mark Lam.
+        
+        AutomaticThread is a new feature in WTF that allows you to easily create worker threads that
+        shut down automatically. This changes DFG::Worklist to use AutomaticThread, so that its
+        threads shut down automatically, too. This has the potential to save a lot of memory.
+        
+        This required some improvements to AutomaticThread: Worklist likes to be able to keep state
+        around for the whole lifetime of a thread, and so it likes knowing when threads are born and
+        when they die. I added virtual methods for that. Also, Worklist uses notifyOne() so I added
+        that, too.
+        
+        This looks to be perf-neutral.
+
+        * dfg/DFGThreadData.cpp:
+        (JSC::DFG::ThreadData::ThreadData):
+        * dfg/DFGThreadData.h:
+        * dfg/DFGWorklist.cpp:
+        (JSC::DFG::Worklist::ThreadBody::ThreadBody):
+        (JSC::DFG::Worklist::Worklist):
+        (JSC::DFG::Worklist::~Worklist):
+        (JSC::DFG::Worklist::finishCreation):
+        (JSC::DFG::Worklist::isActiveForVM):
+        (JSC::DFG::Worklist::enqueue):
+        (JSC::DFG::Worklist::compilationState):
+        (JSC::DFG::Worklist::waitUntilAllPlansForVMAreReady):
+        (JSC::DFG::Worklist::removeAllReadyPlansForVM):
+        (JSC::DFG::Worklist::completeAllReadyPlansForVM):
+        (JSC::DFG::Worklist::rememberCodeBlocks):
+        (JSC::DFG::Worklist::visitWeakReferences):
+        (JSC::DFG::Worklist::removeDeadPlans):
+        (JSC::DFG::Worklist::removeNonCompilingPlansForVM):
+        (JSC::DFG::Worklist::queueLength):
+        (JSC::DFG::Worklist::dump):
+        (JSC::DFG::Worklist::runThread): Deleted.
+        (JSC::DFG::Worklist::threadFunction): Deleted.
+        * dfg/DFGWorklist.h:
+
 2016-10-19  Dan Bernstein  <m...@apple.com>
 
         [Xcode] _javascript_Core fails to build when CLANG_WARN_DOCUMENTATION_COMMENTS is enabled

Modified: trunk/Source/_javascript_Core/dfg/DFGThreadData.cpp (207544 => 207545)


--- trunk/Source/_javascript_Core/dfg/DFGThreadData.cpp	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/_javascript_Core/dfg/DFGThreadData.cpp	2016-10-19 17:47:30 UTC (rev 207545)
@@ -34,7 +34,6 @@
 
 ThreadData::ThreadData(Worklist* worklist)
     : m_worklist(worklist)
-    , m_identifier(0)
     , m_safepoint(nullptr)
 {
 }

Modified: trunk/Source/_javascript_Core/dfg/DFGThreadData.h (207544 => 207545)


--- trunk/Source/_javascript_Core/dfg/DFGThreadData.h	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/_javascript_Core/dfg/DFGThreadData.h	2016-10-19 17:47:30 UTC (rev 207545)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,8 +27,8 @@
 
 #if ENABLE(DFG_JIT)
 
+#include <wtf/AutomaticThread.h>
 #include <wtf/Lock.h>
-#include <wtf/Threading.h>
 
 namespace JSC { namespace DFG {
 
@@ -46,7 +46,7 @@
     friend class Worklist;
     
     Worklist* m_worklist;
-    ThreadIdentifier m_identifier;
+    RefPtr<AutomaticThread> m_thread;
     Lock m_rightToRun;
     Safepoint* m_safepoint;
 };

Modified: trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp (207544 => 207545)


--- trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/_javascript_Core/dfg/DFGWorklist.cpp	2016-10-19 17:47:30 UTC (rev 207545)
@@ -37,8 +37,134 @@
 
 namespace JSC { namespace DFG {
 
+class Worklist::ThreadBody : public AutomaticThread {
+public:
+    ThreadBody(const LockHolder& locker, Worklist& worklist, ThreadData& data, Box<Lock> lock, RefPtr<AutomaticThreadCondition> condition, int relativePriority)
+        : AutomaticThread(locker, lock, condition)
+        , m_worklist(worklist)
+        , m_data(data)
+        , m_relativePriority(relativePriority)
+    {
+    }
+    
+protected:
+    PollResult poll(const LockHolder& locker) override
+    {
+        if (m_worklist.m_queue.isEmpty())
+            return PollResult::Wait;
+        
+        m_plan = m_worklist.m_queue.takeFirst();
+        if (!m_plan) {
+            if (Options::verboseCompilationQueue()) {
+                m_worklist.dump(locker, WTF::dataFile());
+                dataLog(": Thread shutting down\n");
+            }
+            return PollResult::Stop;
+        }
+        RELEASE_ASSERT(m_plan->stage == Plan::Preparing);
+        m_worklist.m_numberOfActiveThreads++;
+        return PollResult::Work;
+    }
+    
+    class WorkScope;
+    friend class WorkScope;
+    class WorkScope {
+    public:
+        WorkScope(ThreadBody& thread)
+            : m_thread(thread)
+        {
+            RELEASE_ASSERT(m_thread.m_plan);
+            RELEASE_ASSERT(m_thread.m_worklist.m_numberOfActiveThreads);
+        }
+        
+        ~WorkScope()
+        {
+            LockHolder locker(*m_thread.m_worklist.m_lock);
+            m_thread.m_plan = nullptr;
+            m_thread.m_worklist.m_numberOfActiveThreads--;
+        }
+        
+    private:
+        ThreadBody& m_thread;
+    };
+    
+    WorkResult work() override
+    {
+        WorkScope workScope(*this);
+        
+        LockHolder locker(m_data.m_rightToRun);
+        {
+            LockHolder locker(*m_worklist.m_lock);
+            if (m_plan->stage == Plan::Cancelled)
+                return WorkResult::Continue;
+            m_plan->notifyCompiling();
+        }
+        
+        if (Options::verboseCompilationQueue())
+            dataLog(m_worklist, ": Compiling ", m_plan->key(), " asynchronously\n");
+        
+        RELEASE_ASSERT(!m_plan->vm->heap.isCollecting());
+        m_plan->compileInThread(*m_longLivedState, &m_data);
+        RELEASE_ASSERT(m_plan->stage == Plan::Cancelled || !m_plan->vm->heap.isCollecting());
+        
+        {
+            LockHolder locker(*m_worklist.m_lock);
+            if (m_plan->stage == Plan::Cancelled)
+                return WorkResult::Continue;
+            
+            m_plan->notifyReady();
+            
+            if (Options::verboseCompilationQueue()) {
+                m_worklist.dump(locker, WTF::dataFile());
+                dataLog(": Compiled ", m_plan->key(), " asynchronously\n");
+            }
+            
+            m_worklist.m_readyPlans.append(m_plan);
+            
+            m_worklist.m_planCompiled.notifyAll();
+        }
+        RELEASE_ASSERT(!m_plan->vm->heap.isCollecting());
+        
+        return WorkResult::Continue;
+    }
+    
+    void threadDidStart() override
+    {
+        if (Options::verboseCompilationQueue())
+            dataLog(m_worklist, ": Thread started\n");
+        
+        if (m_relativePriority)
+            changeThreadPriority(currentThread(), m_relativePriority);
+        
+        m_compilationScope = std::make_unique<CompilationScope>();
+        m_longLivedState = std::make_unique<LongLivedState>();
+    }
+    
+    void threadWillStop() override
+    {
+        if (Options::verboseCompilationQueue())
+            dataLog(m_worklist, ": Thread will stop\n");
+        
+        ASSERT(!m_plan);
+        
+        m_compilationScope = nullptr;
+        m_longLivedState = nullptr;
+        m_plan = nullptr;
+    }
+    
+private:
+    Worklist& m_worklist;
+    ThreadData& m_data;
+    int m_relativePriority;
+    std::unique_ptr<CompilationScope> m_compilationScope;
+    std::unique_ptr<LongLivedState> m_longLivedState;
+    RefPtr<Plan> m_plan;
+};
+
 Worklist::Worklist(CString worklistName)
     : m_threadName(toCString(worklistName, " Worker Thread"))
+    , m_lock(Box<Lock>::create())
+    , m_planEnqueued(AutomaticThreadCondition::create())
     , m_numberOfActiveThreads(0)
 {
 }
@@ -46,13 +172,13 @@
 Worklist::~Worklist()
 {
     {
-        LockHolder locker(m_lock);
+        LockHolder locker(*m_lock);
         for (unsigned i = m_threads.size(); i--;)
             m_queue.append(nullptr); // Use null plan to indicate that we want the thread to terminate.
-        m_planEnqueued.notifyAll();
+        m_planEnqueued->notifyAll(locker);
     }
     for (unsigned i = m_threads.size(); i--;)
-        waitForThreadCompletion(m_threads[i]->m_identifier);
+        m_threads[i]->m_thread->join();
     ASSERT(!m_numberOfActiveThreads);
 }
 
@@ -59,11 +185,10 @@
 void Worklist::finishCreation(unsigned numberOfThreads, int relativePriority)
 {
     RELEASE_ASSERT(numberOfThreads);
+    LockHolder locker(*m_lock);
     for (unsigned i = numberOfThreads; i--;) {
         std::unique_ptr<ThreadData> data = ""
-        data->m_identifier = createThread(threadFunction, data.get(), m_threadName.data());
-        if (relativePriority)
-            changeThreadPriority(data->m_identifier, relativePriority);
+        data->m_thread = adoptRef(new ThreadBody(locker, *this, *data, m_lock, m_planEnqueued, relativePriority));
         m_threads.append(WTFMove(data));
     }
 }
@@ -77,7 +202,7 @@
 
 bool Worklist::isActiveForVM(VM& vm) const
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     PlanMap::const_iterator end = m_plans.end();
     for (PlanMap::const_iterator iter = m_plans.begin(); iter != end; ++iter) {
         if (iter->value->vm == &vm)
@@ -89,7 +214,7 @@
 void Worklist::enqueue(PassRefPtr<Plan> passedPlan)
 {
     RefPtr<Plan> plan = passedPlan;
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     if (Options::verboseCompilationQueue()) {
         dump(locker, WTF::dataFile());
         dataLog(": Enqueueing plan to optimize ", plan->key(), "\n");
@@ -97,12 +222,12 @@
     ASSERT(m_plans.find(plan->key()) == m_plans.end());
     m_plans.add(plan->key(), plan);
     m_queue.append(plan);
-    m_planEnqueued.notifyOne();
+    m_planEnqueued->notifyOne(locker);
 }
 
 Worklist::State Worklist::compilationState(CompilationKey key)
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     PlanMap::iterator iter = m_plans.find(key);
     if (iter == m_plans.end())
         return NotKnown;
@@ -118,7 +243,7 @@
     // After we release this lock, we know that although other VMs may still
     // be adding plans, our VM will not be.
     
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     
     if (Options::verboseCompilationQueue()) {
         dump(locker, WTF::dataFile());
@@ -140,7 +265,7 @@
         if (allAreCompiled)
             break;
         
-        m_planCompiled.wait(m_lock);
+        m_planCompiled.wait(*m_lock);
     }
 }
 
@@ -147,7 +272,7 @@
 void Worklist::removeAllReadyPlansForVM(VM& vm, Vector<RefPtr<Plan>, 8>& myReadyPlans)
 {
     DeferGC deferGC(vm.heap);
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     for (size_t i = 0; i < m_readyPlans.size(); ++i) {
         RefPtr<Plan> plan = m_readyPlans[i];
         if (plan->vm != &vm)
@@ -192,7 +317,7 @@
     }
     
     if (!!requestedKey && resultingState == NotKnown) {
-        LockHolder locker(m_lock);
+        LockHolder locker(*m_lock);
         if (m_plans.contains(requestedKey))
             resultingState = Compiling;
     }
@@ -209,7 +334,7 @@
 
 void Worklist::rememberCodeBlocks(VM& vm)
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     for (PlanMap::iterator iter = m_plans.begin(); iter != m_plans.end(); ++iter) {
         Plan* plan = iter->value.get();
         if (plan->vm != &vm)
@@ -236,7 +361,7 @@
 {
     VM* vm = visitor.heap()->vm();
     {
-        LockHolder locker(m_lock);
+        LockHolder locker(*m_lock);
         for (PlanMap::iterator iter = m_plans.begin(); iter != m_plans.end(); ++iter) {
             Plan* plan = iter->value.get();
             if (plan->vm != vm)
@@ -259,7 +384,7 @@
 void Worklist::removeDeadPlans(VM& vm)
 {
     {
-        LockHolder locker(m_lock);
+        LockHolder locker(*m_lock);
         HashSet<CompilationKey> deadPlanKeys;
         for (PlanMap::iterator iter = m_plans.begin(); iter != m_plans.end(); ++iter) {
             Plan* plan = iter->value.get();
@@ -306,7 +431,7 @@
 
 void Worklist::removeNonCompilingPlansForVM(VM& vm)
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     HashSet<CompilationKey> deadPlanKeys;
     Vector<RefPtr<Plan>> deadPlans;
     for (auto& entry : m_plans) {
@@ -337,13 +462,13 @@
 
 size_t Worklist::queueLength()
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     return m_queue.size();
 }
 
 void Worklist::dump(PrintStream& out) const
 {
-    LockHolder locker(m_lock);
+    LockHolder locker(*m_lock);
     dump(locker, out);
 }
 
@@ -355,83 +480,6 @@
         ", Num Active Threads = ", m_numberOfActiveThreads, "/", m_threads.size(), "]");
 }
 
-void Worklist::runThread(ThreadData* data)
-{
-    CompilationScope compilationScope;
-    
-    if (Options::verboseCompilationQueue())
-        dataLog(*this, ": Thread started\n");
-    
-    LongLivedState longLivedState;
-    
-    for (;;) {
-        RefPtr<Plan> plan;
-        {
-            LockHolder locker(m_lock);
-            while (m_queue.isEmpty())
-                m_planEnqueued.wait(m_lock);
-            
-            plan = m_queue.takeFirst();
-            if (plan) {
-                RELEASE_ASSERT(plan->stage == Plan::Preparing);
-                m_numberOfActiveThreads++;
-            }
-        }
-        
-        if (!plan) {
-            if (Options::verboseCompilationQueue())
-                dataLog(*this, ": Thread shutting down\n");
-            return;
-        }
-        
-        {
-            LockHolder locker(data->m_rightToRun);
-            {
-                LockHolder locker(m_lock);
-                if (plan->stage == Plan::Cancelled) {
-                    m_numberOfActiveThreads--;
-                    continue;
-                }
-                plan->notifyCompiling();
-            }
-        
-            if (Options::verboseCompilationQueue())
-                dataLog(*this, ": Compiling ", plan->key(), " asynchronously\n");
-        
-            RELEASE_ASSERT(!plan->vm->heap.isCollecting());
-            plan->compileInThread(longLivedState, data);
-            RELEASE_ASSERT(plan->stage == Plan::Cancelled || !plan->vm->heap.isCollecting());
-            
-            {
-                LockHolder locker(m_lock);
-                if (plan->stage == Plan::Cancelled) {
-                    m_numberOfActiveThreads--;
-                    continue;
-                }
-                
-                plan->notifyReady();
-                
-                if (Options::verboseCompilationQueue()) {
-                    dump(locker, WTF::dataFile());
-                    dataLog(": Compiled ", plan->key(), " asynchronously\n");
-                }
-                
-                m_readyPlans.append(plan);
-                
-                m_planCompiled.notifyAll();
-                m_numberOfActiveThreads--;
-            }
-            RELEASE_ASSERT(!plan->vm->heap.isCollecting());
-        }
-    }
-}
-
-void Worklist::threadFunction(void* argument)
-{
-    ThreadData* data = ""
-    data->m_worklist->runThread(data);
-}
-
 static Worklist* theGlobalDFGWorklist;
 
 Worklist* ensureGlobalDFGWorklist()

Modified: trunk/Source/_javascript_Core/dfg/DFGWorklist.h (207544 => 207545)


--- trunk/Source/_javascript_Core/dfg/DFGWorklist.h	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/_javascript_Core/dfg/DFGWorklist.h	2016-10-19 17:47:30 UTC (rev 207545)
@@ -29,6 +29,7 @@
 
 #include "DFGPlan.h"
 #include "DFGThreadData.h"
+#include <wtf/AutomaticThread.h>
 #include <wtf/Condition.h>
 #include <wtf/Deque.h>
 #include <wtf/HashMap.h>
@@ -83,6 +84,9 @@
     Worklist(CString worklistName);
     void finishCreation(unsigned numberOfThreads, int);
     
+    class ThreadBody;
+    friend class ThreadBody;
+    
     void runThread(ThreadData*);
     static void threadFunction(void* argument);
     
@@ -108,8 +112,8 @@
 
     Lock m_suspensionLock;
     
-    mutable Lock m_lock;
-    Condition m_planEnqueued;
+    Box<Lock> m_lock;
+    RefPtr<AutomaticThreadCondition> m_planEnqueued;
     Condition m_planCompiled;
     
     Vector<std::unique_ptr<ThreadData>> m_threads;

Modified: trunk/Source/WTF/ChangeLog (207544 => 207545)


--- trunk/Source/WTF/ChangeLog	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/WTF/ChangeLog	2016-10-19 17:47:30 UTC (rev 207545)
@@ -1,3 +1,32 @@
+2016-10-18  Filip Pizlo  <fpi...@apple.com>
+
+        DFG worklist should use AutomaticThread
+        https://bugs.webkit.org/show_bug.cgi?id=163615
+
+        Reviewed by Mark Lam.
+        
+        This adds new functionality to AutomaticThread to support DFG::Worklist:
+        
+        - AutomaticThread::threadDidStart/threadWillStop virtual methods called at the start and end
+          of a thread's lifetime. This allows Worklist to tie some resources to the life of the
+          thread, and also means that now those resources will naturally free up when the Worklist is
+          not in use.
+        
+        - AutomaticThreadCondition::notifyOne(). This required changes to Condition::notifyOne(). We
+          need to know if the Condition woke up anyone. If it didn't, then we need to launch one of
+          our threads.
+
+        * wtf/AutomaticThread.cpp:
+        (WTF::AutomaticThreadCondition::notifyOne):
+        (WTF::AutomaticThread::ThreadScope::ThreadScope):
+        (WTF::AutomaticThread::ThreadScope::~ThreadScope):
+        (WTF::AutomaticThread::start):
+        (WTF::AutomaticThread::threadDidStart):
+        (WTF::AutomaticThread::threadWillStop):
+        * wtf/AutomaticThread.h:
+        * wtf/Condition.h:
+        (WTF::ConditionBase::notifyOne):
+
 2016-10-18  Sam Weinig  <s...@webkit.org>
 
         Replace std::experimental::variant with WTF::Variant (or similar)

Modified: trunk/Source/WTF/wtf/AutomaticThread.cpp (207544 => 207545)


--- trunk/Source/WTF/wtf/AutomaticThread.cpp	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp	2016-10-19 17:47:30 UTC (rev 207545)
@@ -45,6 +45,17 @@
 {
 }
 
+void AutomaticThreadCondition::notifyOne(const LockHolder& locker)
+{
+    if (m_condition.notifyOne())
+        return;
+    
+    if (m_threads.isEmpty())
+        return;
+    
+    m_threads.takeLast()->start(locker);
+}
+
 void AutomaticThreadCondition::notifyAll(const LockHolder& locker)
 {
     m_condition.notifyAll();
@@ -95,6 +106,23 @@
         m_isRunningCondition.wait(*m_lock);
 }
 
+class AutomaticThread::ThreadScope {
+public:
+    ThreadScope(AutomaticThread& thread)
+        : m_thread(thread)
+    {
+        m_thread.threadDidStart();
+    }
+    
+    ~ThreadScope()
+    {
+        m_thread.threadWillStop();
+    }
+
+private:
+    AutomaticThread& m_thread;
+};
+
 void AutomaticThread::start(const LockHolder&)
 {
     RefPtr<AutomaticThread> preserveThisForThread = this;
@@ -111,6 +139,8 @@
                 ASSERT(!m_condition->contains(locker, this));
             }
             
+            ThreadScope threadScope(*this);
+            
             auto stop = [&] (const LockHolder&) {
                 m_isRunning = false;
                 m_isRunningCondition.notifyAll();
@@ -150,5 +180,13 @@
     detachThread(thread);
 }
 
+void AutomaticThread::threadDidStart()
+{
+}
+
+void AutomaticThread::threadWillStop()
+{
+}
+
 } // namespace WTF
 

Modified: trunk/Source/WTF/wtf/AutomaticThread.h (207544 => 207545)


--- trunk/Source/WTF/wtf/AutomaticThread.h	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/WTF/wtf/AutomaticThread.h	2016-10-19 17:47:30 UTC (rev 207545)
@@ -75,6 +75,7 @@
     
     WTF_EXPORT_PRIVATE ~AutomaticThreadCondition();
     
+    WTF_EXPORT_PRIVATE void notifyOne(const LockHolder&);
     WTF_EXPORT_PRIVATE void notifyAll(const LockHolder&);
     
 private:
@@ -108,7 +109,7 @@
     
 protected:
     // This logically creates the thread, but in reality the thread won't be created until someone
-    // calls AutomaticThreadCondition::notifyAll().
+    // calls AutomaticThreadCondition::notifyOne() or notifyAll().
     AutomaticThread(const LockHolder&, Box<Lock>, RefPtr<AutomaticThreadCondition>);
     
     // To understand PollResult and WorkResult, imagine that poll() and work() are being called like
@@ -143,6 +144,15 @@
     enum class WorkResult { Continue, Stop };
     virtual WorkResult work() = 0;
     
+    class ThreadScope;
+    friend class ThreadScope;
+    
+    // It's sometimes useful to allocate resources while the thread is running, and to destroy them
+    // 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();
+    
 private:
     friend class AutomaticThreadCondition;
     
@@ -157,6 +167,7 @@
 } // namespace WTF
 
 using WTF::AutomaticThread;
+using WTF::AutomaticThreadCondition;
 
 #endif // WTF_AutomaticThread_h
 

Modified: trunk/Source/WTF/wtf/Condition.h (207544 => 207545)


--- trunk/Source/WTF/wtf/Condition.h	2016-10-19 17:46:42 UTC (rev 207544)
+++ trunk/Source/WTF/wtf/Condition.h	2016-10-19 17:47:30 UTC (rev 207545)
@@ -168,23 +168,26 @@
     }
 
     // Note that this method is extremely fast when nobody is waiting. It is not necessary to try to
-    // avoid calling this method.
-    void notifyOne()
+    // avoid calling this method. This returns true if someone was actually woken up.
+    bool notifyOne()
     {
         if (!m_hasWaiters.load()) {
             // At this exact instant, there is nobody waiting on this condition. The way to visualize
             // this is that if unparkOne() ran to completion without obstructions at this moment, it
             // wouldn't wake anyone up. Hence, we have nothing to do!
-            return;
+            return false;
         }
         
+        bool didNotifyThread = false;
         ParkingLot::unparkOne(
             &m_hasWaiters,
-            [this] (ParkingLot::UnparkResult result) -> intptr_t {
+            [&] (ParkingLot::UnparkResult result) -> intptr_t {
                 if (!result.mayHaveMoreThreads)
                     m_hasWaiters.store(false);
+                didNotifyThread = result.didUnparkThread;
                 return 0;
             });
+        return didNotifyThread;
     }
     
     void notifyAll()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to