Changeset 191741 in webkit


Ignore:
Timestamp:
Oct 29, 2015 11:45:11 AM (9 years ago)
Author:
ggaren@apple.com
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.

Location:
trunk/Source/bmalloc
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/bmalloc/ChangeLog

    r191196 r191741  
     12015-10-29  Geoffrey Garen  <ggaren@apple.com>
     2
     3        bmalloc: AsyncTask should handle destruction
     4        https://bugs.webkit.org/show_bug.cgi?id=150648
     5
     6        Reviewed by Mark Lam.
     7
     8        So we can use it in more places.
     9
     10        * bmalloc/AsyncTask.h: Use std::thread instead of pthread because it
     11        should be more portable.
     12
     13        (bmalloc::Function>::AsyncTask): Renamed Signaled to RunRequested for
     14        clarity. Added an ExitRequested state.
     15
     16        (bmalloc::Function>::~AsyncTask): Wait for our child thread to exit
     17        before destroying ourselves because our child thread will modify our
     18        data (and might modify our client's data). Note that we only need to
     19        wait for the last child thread since any prior child thread, having
     20        reached the Exited condition, is guaranteed not to read or write any
     21        data.
     22
     23        (bmalloc::Function>::run):
     24        (bmalloc::Function>::runSlowCase): Updated for interface changes. Also
     25        changed to use our WebKit style for condition signal: Hold the lock
     26        during the signal and always notify all. Technically, neither is necessary,
     27        but it is easier to understand the code this way, and harder to make
     28        mistakes.
     29
     30        (bmalloc::Function>::threadEntryPoint):
     31        (bmalloc::Function>::threadRunLoop): Handle the new ExitRequested state.
     32        Technically, this state has no meaningful difference from the Exited
     33        state, but it is nice to be explicit.
     34
     35        (bmalloc::Function>::join): Deleted.
     36        (bmalloc::Function>::pthreadEntryPoint): Deleted.
     37        (bmalloc::Function>::entryPoint): Deleted.
     38
    1392015-10-15  Geoffrey Garen  <ggaren@apple.com>
    240
  • trunk/Source/bmalloc/bmalloc/AsyncTask.h

    r179923 r191741  
    11/*
    2  * Copyright (C) 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3232#include <atomic>
    3333#include <condition_variable>
    34 #include <pthread.h>
    3534#include <thread>
    3635
     
    4140public:
    4241    AsyncTask(Object&, const Function&);
     42    ~AsyncTask();
    4343
    4444    void run();
    45     void join();
    4645
    4746private:
    48     enum State { Exited, Sleeping, Running, Signaled };
     47    enum State { Exited, ExitRequested, Sleeping, Running, RunRequested };
    4948
    5049    static const constexpr std::chrono::seconds exitDelay = std::chrono::seconds(1);
     
    5251    void runSlowCase();
    5352
    54     static void* pthreadEntryPoint(void*);
    55     void entryPoint();
     53    static void threadEntryPoint(AsyncTask*);
     54    void threadRunLoop();
    5655
    5756    std::atomic<State> m_state;
     
    5958    Mutex m_conditionMutex;
    6059    std::condition_variable_any m_condition;
    61     pthread_t m_thread;
     60
     61    std::thread m_thread;
    6262
    6363    Object& m_object;
     
    7878
    7979template<typename Object, typename Function>
    80 void AsyncTask<Object, Function>::join()
     80AsyncTask<Object, Function>::~AsyncTask()
    8181{
    82     if (m_state == Exited)
    83         return;
     82    // Prevent our thread from entering the running or sleeping state.
     83    State oldState = m_state.exchange(ExitRequested);
    8484
    85     { std::lock_guard<Mutex> lock(m_conditionMutex); }
    86     m_condition.notify_one();
     85    // Wake our thread if it was already in the sleeping state.
     86    if (oldState == Sleeping) {
     87        std::lock_guard<Mutex> lock(m_conditionMutex);
     88        m_condition.notify_all();
     89    }
    8790
    88     while (m_state != Exited)
    89         std::this_thread::yield();
     91    // Wait for our thread to exit because it uses our data members (and it may
     92    // use m_object's data members).
     93    if (m_thread.joinable())
     94        m_thread.join();
    9095}
    9196
     
    9398inline void AsyncTask<Object, Function>::run()
    9499{
    95     if (m_state == Signaled)
     100    if (m_state == RunRequested)
    96101        return;
    97102    runSlowCase();
     
    101106NO_INLINE void AsyncTask<Object, Function>::runSlowCase()
    102107{
    103     State oldState = m_state.exchange(Signaled);
    104     if (oldState == Signaled || oldState == Running)
     108    State oldState = m_state.exchange(RunRequested);
     109    if (oldState == RunRequested || oldState == Running)
    105110        return;
    106111
    107112    if (oldState == Sleeping) {
    108         { std::lock_guard<Mutex> lock(m_conditionMutex); }
    109         m_condition.notify_one();
     113        std::lock_guard<Mutex> lock(m_conditionMutex);
     114        m_condition.notify_all();
    110115        return;
    111116    }
    112117
    113118    BASSERT(oldState == Exited);
    114     pthread_create(&m_thread, nullptr, &pthreadEntryPoint, this);
    115     pthread_detach(m_thread);
     119    if (m_thread.joinable())
     120        m_thread.detach();
     121    m_thread = std::thread(&AsyncTask::threadEntryPoint, this);
    116122}
    117123
    118124template<typename Object, typename Function>
    119 void* AsyncTask<Object, Function>::pthreadEntryPoint(void* asyncTask)
     125void AsyncTask<Object, Function>::threadEntryPoint(AsyncTask* asyncTask)
    120126{
    121     static_cast<AsyncTask*>(asyncTask)->entryPoint();
    122     return nullptr;
     127    asyncTask->threadRunLoop();
    123128}
    124129
    125130template<typename Object, typename Function>
    126 void AsyncTask<Object, Function>::entryPoint()
     131void AsyncTask<Object, Function>::threadRunLoop()
    127132{
     133    // This loop ratchets downward from most active to least active state, and
     134    // finally exits. While we ratchet downward, any other thread may reset our
     135    // state to RunRequested or ExitRequested.
     136   
     137    // We require any state change while we are sleeping to signal to our
     138    // condition variable and wake us up.
     139
    128140    while (1) {
    129         State expectedState = Signaled;
     141        State expectedState = RunRequested;
    130142        if (m_state.compare_exchange_weak(expectedState, Running))
    131143            (m_object.*m_function)();
     
    140152        if (m_state.compare_exchange_weak(expectedState, Exited))
    141153            return;
     154       
     155        expectedState = ExitRequested;
     156        if (m_state.compare_exchange_weak(expectedState, Exited))
     157            return;
    142158    }
    143159}
Note: See TracChangeset for help on using the changeset viewer.