Changeset 252687 in webkit
- Timestamp:
- Nov 20, 2019 8:04:52 AM (4 years ago)
- Location:
- trunk/Source/WTF
- Files:
-
- 1 deleted
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WTF/ChangeLog
r252683 r252687 1 2019-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 1 40 2019-11-19 Ross Kirsling <ross.kirsling@sony.com> 2 41 -
trunk/Source/WTF/wtf/PlatformWin.cmake
r246490 r252687 22 22 win/PathWalker.cpp 23 23 win/RunLoopWin.cpp 24 win/ThreadSpecificWin.cpp25 24 win/ThreadingWin.cpp 26 25 win/WorkQueueWin.cpp -
trunk/Source/WTF/wtf/ThreadSpecific.h
r248546 r252687 101 101 T* set(); 102 102 void setInTLS(Data*); 103 void static THREAD_SPECIFIC_CALLdestroy(void* ptr);103 void static destroy(void* ptr); 104 104 105 105 #if USE(PTHREADS) 106 106 pthread_key_t m_key { }; 107 107 #elif OS(WINDOWS) 108 int m_ index;108 int m_key; 109 109 #endif 110 110 }; … … 137 137 #elif OS(WINDOWS) 138 138 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 147 139 template<typename T, CanBeGCThread canBeGCThread> 148 140 inline 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) 153 145 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]);165 146 } 166 147 … … 168 149 inline T* ThreadSpecific<T, canBeGCThread>::get() 169 150 { 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(); 174 155 } 175 156 … … 177 158 inline void ThreadSpecific<T, canBeGCThread>::setInTLS(Data* data) 178 159 { 179 FlsSetValue(flsKeys()[m_index], data);160 return Thread::current().specificStorage().set(m_key, data); 180 161 } 181 162 … … 185 166 186 167 template<typename T, CanBeGCThread canBeGCThread> 187 inline void T HREAD_SPECIFIC_CALL ThreadSpecific<T, canBeGCThread>::destroy(void* ptr)168 inline void ThreadSpecific<T, canBeGCThread>::destroy(void* ptr) 188 169 { 189 170 Data* data = static_cast<Data*>(ptr); -
trunk/Source/WTF/wtf/Threading.h
r251263 r252687 53 53 #endif 54 54 55 #if OS(WINDOWS) 56 #include <array> 57 #endif 58 55 59 namespace WTF { 56 60 … … 107 111 108 112 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; }; 109 130 #endif 110 131 … … 270 291 static Thread* currentMayBeNull(); 271 292 272 #if OS(WINDOWS)273 WTF_EXPORT_PRIVATE static Thread* currentDying();274 static RefPtr<Thread> get(ThreadIdentifier);275 #endif276 277 293 // This thread-specific destructor is called 2 times when thread terminates: 278 294 // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once' 279 295 // 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 removethe281 // 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. 282 298 static void THREAD_SPECIFIC_CALL destructTLS(void* data); 283 299 … … 304 320 #endif 305 321 322 #if OS(WINDOWS) 323 SpecificStorage m_specificStorage; 324 #endif 325 306 326 AtomStringTable* m_currentAtomStringTable { nullptr }; 307 327 AtomStringTable m_defaultAtomStringTable; … … 349 369 if (auto* thread = currentMayBeNull()) 350 370 return *thread; 351 #if OS(WINDOWS)352 if (auto* thread = currentDying())353 return *thread;354 #endif355 371 return initializeCurrentTLS(); 356 372 } -
trunk/Source/WTF/wtf/win/ThreadingWin.cpp
r245260 r252687 266 266 #define InvalidThread reinterpret_cast<Thread*>(static_cast<uintptr_t>(0xbbadbeef)) 267 267 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 276 268 void Thread::initializeTLSKey() 277 269 { 278 threadMap();279 270 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;297 271 } 298 272 … … 305 279 auto& threadInTLS = thread.leakRef(); 306 280 threadSpecificSet(s_key, &threadInTLS); 307 {308 auto locker = holdLock(threadMapMutex);309 threadMap().add(id, &threadInTLS);310 }311 281 return threadInTLS; 312 282 } … … 320 290 ASSERT(thread); 321 291 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 302 Atomic<int> Thread::SpecificStorage::s_numberOfKeys; 303 std::array<Atomic<Thread::SpecificStorage::DestroyFunction>, Thread::SpecificStorage::s_maxKeys> Thread::SpecificStorage::s_destroyFunctions; 304 305 bool 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 317 void* Thread::SpecificStorage::get(int key) 318 { 319 return m_slots[key]; 320 } 321 322 void Thread::SpecificStorage::set(int key, void* value) 323 { 324 m_slots[key] = value; 325 } 326 327 void 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; 329 335 } 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 to351 // detect incorrect use of Thread::current() after this point because it will crash.352 threadSpecificSet(s_key, InvalidThread);353 return;354 336 } 355 threadSpecificSet(s_key, InvalidThread);356 thread->m_isDestroyedOnce = true;357 337 } 358 338
Note: See TracChangeset
for help on using the changeset viewer.