Title: [191741] trunk/Source/bmalloc
Revision
191741
Author
gga...@apple.com
Date
2015-10-29 11:45:11 -0700 (Thu, 29 Oct 2015)

Log Message

bmalloc: AsyncTask should handle destruction
https://bugs.webkit.org/show_bug.cgi?id=150648

Reviewed by Mark Lam.

So we can use it in more places.

* bmalloc/AsyncTask.h: Use std::thread instead of pthread because it
should be more portable.

(bmalloc::Function>::AsyncTask): Renamed Signaled to RunRequested for
clarity. Added an ExitRequested state.

(bmalloc::Function>::~AsyncTask): Wait for our child thread to exit
before destroying ourselves because our child thread will modify our
data (and might modify our client's data). Note that we only need to
wait for the last child thread since any prior child thread, having
reached the Exited condition, is guaranteed not to read or write any
data.

(bmalloc::Function>::run):
(bmalloc::Function>::runSlowCase): Updated for interface changes. Also
changed to use our WebKit style for condition signal: Hold the lock
during the signal and always notify all. Technically, neither is necessary,
but it is easier to understand the code this way, and harder to make
mistakes.

(bmalloc::Function>::threadEntryPoint):
(bmalloc::Function>::threadRunLoop): Handle the new ExitRequested state.
Technically, this state has no meaningful difference from the Exited
state, but it is nice to be explicit.

(bmalloc::Function>::join): Deleted.
(bmalloc::Function>::pthreadEntryPoint): Deleted.
(bmalloc::Function>::entryPoint): Deleted.

Modified Paths

Diff

Modified: trunk/Source/bmalloc/ChangeLog (191740 => 191741)


--- trunk/Source/bmalloc/ChangeLog	2015-10-29 18:35:17 UTC (rev 191740)
+++ trunk/Source/bmalloc/ChangeLog	2015-10-29 18:45:11 UTC (rev 191741)
@@ -1,3 +1,41 @@
+2015-10-29  Geoffrey Garen  <gga...@apple.com>
+
+        bmalloc: AsyncTask should handle destruction
+        https://bugs.webkit.org/show_bug.cgi?id=150648
+
+        Reviewed by Mark Lam.
+
+        So we can use it in more places.
+
+        * bmalloc/AsyncTask.h: Use std::thread instead of pthread because it
+        should be more portable.
+
+        (bmalloc::Function>::AsyncTask): Renamed Signaled to RunRequested for
+        clarity. Added an ExitRequested state.
+
+        (bmalloc::Function>::~AsyncTask): Wait for our child thread to exit
+        before destroying ourselves because our child thread will modify our
+        data (and might modify our client's data). Note that we only need to
+        wait for the last child thread since any prior child thread, having
+        reached the Exited condition, is guaranteed not to read or write any
+        data.
+
+        (bmalloc::Function>::run):
+        (bmalloc::Function>::runSlowCase): Updated for interface changes. Also
+        changed to use our WebKit style for condition signal: Hold the lock
+        during the signal and always notify all. Technically, neither is necessary,
+        but it is easier to understand the code this way, and harder to make
+        mistakes.
+
+        (bmalloc::Function>::threadEntryPoint):
+        (bmalloc::Function>::threadRunLoop): Handle the new ExitRequested state.
+        Technically, this state has no meaningful difference from the Exited
+        state, but it is nice to be explicit.
+
+        (bmalloc::Function>::join): Deleted.
+        (bmalloc::Function>::pthreadEntryPoint): Deleted.
+        (bmalloc::Function>::entryPoint): Deleted.
+
 2015-10-15  Geoffrey Garen  <gga...@apple.com>
 
         bmalloc: per-thread cache data structure should be smaller

Modified: trunk/Source/bmalloc/bmalloc/AsyncTask.h (191740 => 191741)


--- trunk/Source/bmalloc/bmalloc/AsyncTask.h	2015-10-29 18:35:17 UTC (rev 191740)
+++ trunk/Source/bmalloc/bmalloc/AsyncTask.h	2015-10-29 18:45:11 UTC (rev 191741)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,7 +31,6 @@
 #include "Mutex.h"
 #include <atomic>
 #include <condition_variable>
-#include <pthread.h>
 #include <thread>
 
 namespace bmalloc {
@@ -40,26 +39,27 @@
 class AsyncTask {
 public:
     AsyncTask(Object&, const Function&);
+    ~AsyncTask();
 
     void run();
-    void join();
 
 private:
-    enum State { Exited, Sleeping, Running, Signaled };
+    enum State { Exited, ExitRequested, Sleeping, Running, RunRequested };
 
     static const constexpr std::chrono::seconds exitDelay = std::chrono::seconds(1);
 
     void runSlowCase();
 
-    static void* pthreadEntryPoint(void*);
-    void entryPoint();
+    static void threadEntryPoint(AsyncTask*);
+    void threadRunLoop();
 
     std::atomic<State> m_state;
 
     Mutex m_conditionMutex;
     std::condition_variable_any m_condition;
-    pthread_t m_thread;
 
+    std::thread m_thread;
+
     Object& m_object;
     Function m_function;
 };
@@ -77,22 +77,27 @@
 }
 
 template<typename Object, typename Function>
-void AsyncTask<Object, Function>::join()
+AsyncTask<Object, Function>::~AsyncTask()
 {
-    if (m_state == Exited)
-        return;
+    // Prevent our thread from entering the running or sleeping state.
+    State oldState = m_state.exchange(ExitRequested);
 
-    { std::lock_guard<Mutex> lock(m_conditionMutex); }
-    m_condition.notify_one();
+    // Wake our thread if it was already in the sleeping state.
+    if (oldState == Sleeping) {
+        std::lock_guard<Mutex> lock(m_conditionMutex);
+        m_condition.notify_all();
+    }
 
-    while (m_state != Exited)
-        std::this_thread::yield();
+    // Wait for our thread to exit because it uses our data members (and it may
+    // use m_object's data members).
+    if (m_thread.joinable())
+        m_thread.join();
 }
 
 template<typename Object, typename Function>
 inline void AsyncTask<Object, Function>::run()
 {
-    if (m_state == Signaled)
+    if (m_state == RunRequested)
         return;
     runSlowCase();
 }
@@ -100,33 +105,40 @@
 template<typename Object, typename Function>
 NO_INLINE void AsyncTask<Object, Function>::runSlowCase()
 {
-    State oldState = m_state.exchange(Signaled);
-    if (oldState == Signaled || oldState == Running)
+    State oldState = m_state.exchange(RunRequested);
+    if (oldState == RunRequested || oldState == Running)
         return;
 
     if (oldState == Sleeping) {
-        { std::lock_guard<Mutex> lock(m_conditionMutex); }
-        m_condition.notify_one();
+        std::lock_guard<Mutex> lock(m_conditionMutex);
+        m_condition.notify_all();
         return;
     }
 
     BASSERT(oldState == Exited);
-    pthread_create(&m_thread, nullptr, &pthreadEntryPoint, this);
-    pthread_detach(m_thread);
+    if (m_thread.joinable())
+        m_thread.detach();
+    m_thread = std::thread(&AsyncTask::threadEntryPoint, this);
 }
 
 template<typename Object, typename Function>
-void* AsyncTask<Object, Function>::pthreadEntryPoint(void* asyncTask)
+void AsyncTask<Object, Function>::threadEntryPoint(AsyncTask* asyncTask)
 {
-    static_cast<AsyncTask*>(asyncTask)->entryPoint();
-    return nullptr;
+    asyncTask->threadRunLoop();
 }
 
 template<typename Object, typename Function>
-void AsyncTask<Object, Function>::entryPoint()
+void AsyncTask<Object, Function>::threadRunLoop()
 {
+    // This loop ratchets downward from most active to least active state, and
+    // finally exits. While we ratchet downward, any other thread may reset our
+    // state to RunRequested or ExitRequested.
+    
+    // We require any state change while we are sleeping to signal to our
+    // condition variable and wake us up.
+
     while (1) {
-        State expectedState = Signaled;
+        State expectedState = RunRequested;
         if (m_state.compare_exchange_weak(expectedState, Running))
             (m_object.*m_function)();
 
@@ -139,6 +151,10 @@
         expectedState = Sleeping;
         if (m_state.compare_exchange_weak(expectedState, Exited))
             return;
+        
+        expectedState = ExitRequested;
+        if (m_state.compare_exchange_weak(expectedState, Exited))
+            return;
     }
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to