Changeset 252687 in webkit


Ignore:
Timestamp:
Nov 20, 2019 8:04:52 AM (4 years ago)
Author:
Fujii Hironori
Message:

[Win] Implement WTF::ThreadSpecific in WTF::Thread
https://bugs.webkit.org/show_bug.cgi?id=204341

Reviewed by Brent Fulgham and Yusuke Suzuki.

Thread::destructTLS had a tricky code to defer destroying
WTF::Thread in TLS in order to ensure WTF::Thread is destructed
after other ThreadSpecific are destructed, which is a part of
cause of nasty hanging issue in the process termination (Bug 204192).

This change implements WTF::ThreadSpecific in WTF::Thread by
adding a new class Thread::SpecificStorage to manage TLS. Simplify
Thread::destructTLS. Remove threadMapMutex in ThreadingWin.cpp

  • wtf/PlatformWin.cmake:
  • wtf/ThreadSpecific.h:

(WTF::canBeGCThread>::ThreadSpecific):
(WTF::canBeGCThread>::get):
(WTF::canBeGCThread>::setInTLS):
(WTF::canBeGCThread>::destroy):
(WTF::canBeGCThread>::~ThreadSpecific): Deleted.

  • wtf/Threading.h:

(WTF::Thread::specificStorage):
(WTF::Thread::current):

  • wtf/win/ThreadSpecificWin.cpp: Removed.
  • wtf/win/ThreadingWin.cpp:

(WTF::Thread::initializeTLSKey):
(WTF::Thread::initializeTLS):
(WTF::Thread::destructTLS):
(WTF::Thread::SpecificStorage::allocateKey):
(WTF::Thread::SpecificStorage::get):
(WTF::Thread::SpecificStorage::set):
(WTF::Thread::SpecificStorage::destroySlots):
(): Deleted.
(WTF::Thread::currentDying): Deleted.
(WTF::Thread::get): Deleted.

Location:
trunk/Source/WTF
Files:
1 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r252683 r252687  
     12019-11-20  Fujii Hironori  <Hironori.Fujii@sony.com>
     2
     3        [Win] Implement WTF::ThreadSpecific in WTF::Thread
     4        https://bugs.webkit.org/show_bug.cgi?id=204341
     5
     6        Reviewed by Brent Fulgham and Yusuke Suzuki.
     7
     8        Thread::destructTLS had a tricky code to defer destroying
     9        WTF::Thread in TLS in order to ensure WTF::Thread is destructed
     10        after other ThreadSpecific are destructed, which is a part of
     11        cause of nasty hanging issue in the process termination (Bug 204192).
     12
     13        This change implements WTF::ThreadSpecific in WTF::Thread by
     14        adding a new class Thread::SpecificStorage to manage TLS. Simplify
     15        Thread::destructTLS. Remove threadMapMutex in ThreadingWin.cpp
     16
     17        * wtf/PlatformWin.cmake:
     18        * wtf/ThreadSpecific.h:
     19        (WTF::canBeGCThread>::ThreadSpecific):
     20        (WTF::canBeGCThread>::get):
     21        (WTF::canBeGCThread>::setInTLS):
     22        (WTF::canBeGCThread>::destroy):
     23        (WTF::canBeGCThread>::~ThreadSpecific): Deleted.
     24        * wtf/Threading.h:
     25        (WTF::Thread::specificStorage):
     26        (WTF::Thread::current):
     27        * wtf/win/ThreadSpecificWin.cpp: Removed.
     28        * wtf/win/ThreadingWin.cpp:
     29        (WTF::Thread::initializeTLSKey):
     30        (WTF::Thread::initializeTLS):
     31        (WTF::Thread::destructTLS):
     32        (WTF::Thread::SpecificStorage::allocateKey):
     33        (WTF::Thread::SpecificStorage::get):
     34        (WTF::Thread::SpecificStorage::set):
     35        (WTF::Thread::SpecificStorage::destroySlots):
     36        (): Deleted.
     37        (WTF::Thread::currentDying): Deleted.
     38        (WTF::Thread::get): Deleted.
     39
    1402019-11-19  Ross Kirsling  <ross.kirsling@sony.com>
    241
  • trunk/Source/WTF/wtf/PlatformWin.cmake

    r246490 r252687  
    2222    win/PathWalker.cpp
    2323    win/RunLoopWin.cpp
    24     win/ThreadSpecificWin.cpp
    2524    win/ThreadingWin.cpp
    2625    win/WorkQueueWin.cpp
  • trunk/Source/WTF/wtf/ThreadSpecific.h

    r248546 r252687  
    101101    T* set();
    102102    void setInTLS(Data*);
    103     void static THREAD_SPECIFIC_CALL destroy(void* ptr);
     103    void static destroy(void* ptr);
    104104
    105105#if USE(PTHREADS)
    106106    pthread_key_t m_key { };
    107107#elif OS(WINDOWS)
    108     int m_index;
     108    int m_key;
    109109#endif
    110110};
     
    137137#elif OS(WINDOWS)
    138138
    139 // The maximum number of FLS keys that can be created. For simplification, we assume that:
    140 // 1) Once the instance of ThreadSpecific<> is created, it will not be destructed until the program dies.
    141 // 2) We do not need to hold many instances of ThreadSpecific<> data. This fixed number should be far enough.
    142 static constexpr int maxFlsKeySize = 128;
    143 
    144 WTF_EXPORT_PRIVATE long& flsKeyCount();
    145 WTF_EXPORT_PRIVATE DWORD* flsKeys();
    146 
    147139template<typename T, CanBeGCThread canBeGCThread>
    148140inline ThreadSpecific<T, canBeGCThread>::ThreadSpecific()
    149     : m_index(-1)
    150 {
    151     DWORD flsKey = FlsAlloc(destroy);
    152     if (flsKey == FLS_OUT_OF_INDEXES)
     141    : m_key(-1)
     142{
     143    bool ok = Thread::SpecificStorage::allocateKey(m_key, destroy);
     144    if (!ok)
    153145        CRASH();
    154 
    155     m_index = InterlockedIncrement(&flsKeyCount()) - 1;
    156     if (m_index >= maxFlsKeySize)
    157         CRASH();
    158     flsKeys()[m_index] = flsKey;
    159 }
    160 
    161 template<typename T, CanBeGCThread canBeGCThread>
    162 inline ThreadSpecific<T, canBeGCThread>::~ThreadSpecific()
    163 {
    164     FlsFree(flsKeys()[m_index]);
    165146}
    166147
     
    168149inline T* ThreadSpecific<T, canBeGCThread>::get()
    169150{
    170     Data* data = static_cast<Data*>(FlsGetValue(flsKeys()[m_index]));
    171     if (data)
    172         return data->storagePointer();
    173     return nullptr;
     151    auto data = static_cast<Data*>(Thread::current().specificStorage().get(m_key));
     152    if (!data)
     153        return nullptr;
     154    return data->storagePointer();
    174155}
    175156
     
    177158inline void ThreadSpecific<T, canBeGCThread>::setInTLS(Data* data)
    178159{
    179     FlsSetValue(flsKeys()[m_index], data);
     160    return Thread::current().specificStorage().set(m_key, data);
    180161}
    181162
     
    185166
    186167template<typename T, CanBeGCThread canBeGCThread>
    187 inline void THREAD_SPECIFIC_CALL ThreadSpecific<T, canBeGCThread>::destroy(void* ptr)
     168inline void ThreadSpecific<T, canBeGCThread>::destroy(void* ptr)
    188169{
    189170    Data* data = static_cast<Data*>(ptr);
  • trunk/Source/WTF/wtf/Threading.h

    r251263 r252687  
    5353#endif
    5454
     55#if OS(WINDOWS)
     56#include <array>
     57#endif
     58
    5559namespace WTF {
    5660
     
    107111
    108112    ThreadIdentifier id() const { return m_id; }
     113
     114    class SpecificStorage {
     115    public:
     116        using DestroyFunction = void (*)(void*);
     117        WTF_EXPORT_PRIVATE static bool allocateKey(int& key, DestroyFunction);
     118        WTF_EXPORT_PRIVATE void* get(int key);
     119        WTF_EXPORT_PRIVATE void set(int key, void* value);
     120        void destroySlots();
     121
     122    private:
     123        static constexpr size_t s_maxKeys = 32;
     124        static Atomic<int> s_numberOfKeys;
     125        static std::array<Atomic<DestroyFunction>, s_maxKeys> s_destroyFunctions;
     126        std::array<void*, s_maxKeys> m_slots { };
     127    };
     128
     129    SpecificStorage& specificStorage() { return m_specificStorage; };
    109130#endif
    110131
     
    270291    static Thread* currentMayBeNull();
    271292
    272 #if OS(WINDOWS)
    273     WTF_EXPORT_PRIVATE static Thread* currentDying();
    274     static RefPtr<Thread> get(ThreadIdentifier);
    275 #endif
    276 
    277293    // This thread-specific destructor is called 2 times when thread terminates:
    278294    // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once'
    279295    // and (1) re-sets itself into the thread-specific slot or (2) constructs thread local value to call it again later.
    280     // - second, after all thread-specific destructors were invoked, it gets called again - this time, we remove the
    281     // Thread from the threadMap, completing the cleanup.
     296    // - second, after all thread-specific destructors were invoked, it gets called again - this time, we deref the
     297    // Thread in the TLS, completing the cleanup.
    282298    static void THREAD_SPECIFIC_CALL destructTLS(void* data);
    283299
     
    304320#endif
    305321
     322#if OS(WINDOWS)
     323    SpecificStorage m_specificStorage;
     324#endif
     325
    306326    AtomStringTable* m_currentAtomStringTable { nullptr };
    307327    AtomStringTable m_defaultAtomStringTable;
     
    349369    if (auto* thread = currentMayBeNull())
    350370        return *thread;
    351 #if OS(WINDOWS)
    352     if (auto* thread = currentDying())
    353         return *thread;
    354 #endif
    355371    return initializeCurrentTLS();
    356372}
  • trunk/Source/WTF/wtf/win/ThreadingWin.cpp

    r245260 r252687  
    266266#define InvalidThread reinterpret_cast<Thread*>(static_cast<uintptr_t>(0xbbadbeef))
    267267
    268 static WordLock threadMapMutex;
    269 
    270 static HashMap<ThreadIdentifier, Thread*>& threadMap()
    271 {
    272     static NeverDestroyed<HashMap<ThreadIdentifier, Thread*>> map;
    273     return map.get();
    274 }
    275 
    276268void Thread::initializeTLSKey()
    277269{
    278     threadMap();
    279270    threadSpecificKeyCreate(&s_key, destructTLS);
    280 }
    281 
    282 Thread* Thread::currentDying()
    283 {
    284     ASSERT(s_key != InvalidThreadSpecificKey);
    285     // After FLS is destroyed, this map offers the value until the second thread exit callback is called.
    286     auto locker = holdLock(threadMapMutex);
    287     return threadMap().get(currentID());
    288 }
    289 
    290 RefPtr<Thread> Thread::get(ThreadIdentifier id)
    291 {
    292     auto locker = holdLock(threadMapMutex);
    293     Thread* thread = threadMap().get(id);
    294     if (thread)
    295         return thread;
    296     return nullptr;
    297271}
    298272
     
    305279    auto& threadInTLS = thread.leakRef();
    306280    threadSpecificSet(s_key, &threadInTLS);
    307     {
    308         auto locker = holdLock(threadMapMutex);
    309         threadMap().add(id, &threadInTLS);
    310     }
    311281    return threadInTLS;
    312282}
     
    320290    ASSERT(thread);
    321291
    322     // Delay the deallocation of Thread more.
    323     // It defers Thread deallocation after the other ThreadSpecific values are deallocated.
    324     static thread_local class ThreadExitCallback {
    325     public:
    326         ThreadExitCallback(Thread* thread)
    327             : m_thread(thread)
    328         {
     292    thread->specificStorage().destroySlots();
     293    thread->didExit();
     294    thread->deref();
     295
     296    // Fill the FLS with the non-nullptr value. While FLS destructor won't be called for that,
     297    // non-nullptr value tells us that we already destructed Thread. This allows us to
     298    // detect incorrect use of Thread::current() after this point because it will crash.
     299    threadSpecificSet(s_key, InvalidThread);
     300}
     301
     302Atomic<int> Thread::SpecificStorage::s_numberOfKeys;
     303std::array<Atomic<Thread::SpecificStorage::DestroyFunction>, Thread::SpecificStorage::s_maxKeys> Thread::SpecificStorage::s_destroyFunctions;
     304
     305bool Thread::SpecificStorage::allocateKey(int& key, DestroyFunction destroy)
     306{
     307    int k = s_numberOfKeys.exchangeAdd(1);
     308    if (k >= s_maxKeys) {
     309        s_numberOfKeys.exchangeSub(1);
     310        return false;
     311    }
     312    key = k;
     313    s_destroyFunctions[key].store(destroy);
     314    return true;
     315}
     316
     317void* Thread::SpecificStorage::get(int key)
     318{
     319    return m_slots[key];
     320}
     321
     322void Thread::SpecificStorage::set(int key, void* value)
     323{
     324    m_slots[key] = value;
     325}
     326
     327void Thread::SpecificStorage::destroySlots()
     328{
     329    auto numberOfKeys = s_numberOfKeys.load();
     330    for (size_t i = 0; i < numberOfKeys; i++) {
     331        auto destroy = s_destroyFunctions[i].load();
     332        if (destroy && m_slots[i]) {
     333            destroy(m_slots[i]);
     334            m_slots[i] = nullptr;
    329335        }
    330 
    331         ~ThreadExitCallback()
    332         {
    333             Thread::destructTLS(m_thread);
    334         }
    335 
    336     private:
    337         Thread* m_thread;
    338     } callback(thread);
    339 
    340     if (thread->m_isDestroyedOnce) {
    341         {
    342             auto locker = holdLock(threadMapMutex);
    343             ASSERT(threadMap().contains(thread->id()));
    344             threadMap().remove(thread->id());
    345         }
    346         thread->didExit();
    347         thread->deref();
    348 
    349         // Fill the FLS with the non-nullptr value. While FLS destructor won't be called for that,
    350         // non-nullptr value tells us that we already destructed Thread. This allows us to
    351         // detect incorrect use of Thread::current() after this point because it will crash.
    352         threadSpecificSet(s_key, InvalidThread);
    353         return;
    354336    }
    355     threadSpecificSet(s_key, InvalidThread);
    356     thread->m_isDestroyedOnce = true;
    357337}
    358338
Note: See TracChangeset for help on using the changeset viewer.