Source/WTF/ChangeLog

 12019-11-19 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 NOBODY (OOPS!).
 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::SpecificStorage):
 33 (WTF::Thread::SpecificStorage::allocKey):
 34 (WTF::Thread::SpecificStorage::get):
 35 (WTF::Thread::SpecificStorage::set):
 36 (WTF::Thread::SpecificStorage::destroySlots):
 37 (): Deleted.
 38 (WTF::Thread::currentDying): Deleted.
 39 (WTF::Thread::get): Deleted.
 40
1412019-11-18 Fujii Hironori <Hironori.Fujii@sony.com>
242
343 Remove self-defined C++14 features in StdLibExtras.h

Source/WTF/wtf/PlatformWin.cmake

@@list(APPEND WTF_SOURCES
2121 win/OSAllocatorWin.cpp
2222 win/PathWalker.cpp
2323 win/RunLoopWin.cpp
24  win/ThreadSpecificWin.cpp
2524 win/ThreadingWin.cpp
2625 win/WorkQueueWin.cpp
2726)

Source/WTF/wtf/ThreadSpecific.h

@@private:
100100 T* get();
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};
111111

@@inline void ThreadSpecific<T, canBeGCThread>::setInTLS(Data* data)
136136
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)
 141 : m_key(-1)
150142{
151  DWORD flsKey = FlsAlloc(destroy);
152  if (flsKey == FLS_OUT_OF_INDEXES)
153  CRASH();
154 
155  m_index = InterlockedIncrement(&flsKeyCount()) - 1;
156  if (m_index >= maxFlsKeySize)
 143 bool ok = Thread::SpecificStorage::allocKey(m_key, destroy);
 144 if (!ok)
157145 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
167148template<typename T, CanBeGCThread canBeGCThread>
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
176157template<typename T, CanBeGCThread canBeGCThread>
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
182163#else

@@inline void ThreadSpecific<T, canBeGCThread>::setInTLS(Data* data)
184165#endif
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);
190171

Source/WTF/wtf/Threading.h

5252#include <signal.h>
5353#endif
5454
 55#if OS(WINDOWS)
 56#include <array>
 57#endif
 58
5559namespace WTF {
5660
5761class AbstractLocker;

@@public:
106110 WTF_EXPORT_PRIVATE static ThreadIdentifier currentID();
107111
108112 ThreadIdentifier id() const { return m_id; }
 113
 114 class SpecificStorage {
 115 public:
 116 SpecificStorage();
 117 using DestroyFunction = void (*)(void*);
 118 WTF_EXPORT_PRIVATE static bool allocKey(int& key, DestroyFunction);
 119 WTF_EXPORT_PRIVATE void* get(int key);
 120 WTF_EXPORT_PRIVATE void set(int key, void* value);
 121 void destroySlots();
 122
 123 private:
 124 static constexpr size_t s_maxKeys = 32;
 125 static Atomic<int> s_numberOfKeys;
 126 static std::array<Atomic<DestroyFunction>, s_maxKeys> s_destroyFunctions;
 127 std::array<void*, s_maxKeys> m_slots;
 128 };
 129
 130 SpecificStorage& specificStorage() { return m_specificStorage; };
109131#endif
110132
111133 WTF_EXPORT_PRIVATE void changePriority(int);

@@protected:
269291 // Returns nullptr if thread-specific storage was not initialized.
270292 static Thread* currentMayBeNull();
271293
272 #if OS(WINDOWS)
273  WTF_EXPORT_PRIVATE static Thread* currentDying();
274  static RefPtr<Thread> get(ThreadIdentifier);
275 #endif
276 
277294 // This thread-specific destructor is called 2 times when thread terminates:
278295 // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once'
279296 // 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.
 297 // - second, after all thread-specific destructors were invoked, it gets called again - this time, we deref the
 298 // Thread in the TLS, completing the cleanup.
282299 static void THREAD_SPECIFIC_CALL destructTLS(void* data);
283300
284301 JoinableState m_joinableState { Joinable };

@@protected:
303320 unsigned m_suspendCount { 0 };
304321#endif
305322
 323#if OS(WINDOWS)
 324 SpecificStorage m_specificStorage;
 325#endif
 326
306327 AtomStringTable* m_currentAtomStringTable { nullptr };
307328 AtomStringTable m_defaultAtomStringTable;
308329

@@inline Thread& Thread::current()
348369#endif
349370 if (auto* thread = currentMayBeNull())
350371 return *thread;
351 #if OS(WINDOWS)
352  if (auto* thread = currentDying())
353  return *thread;
354 #endif
355372 return initializeCurrentTLS();
356373}
357374

Source/WTF/wtf/win/ThreadSpecificWin.cpp

1 /*
2  * Copyright (C) 2009 Jian Li <jianli@chromium.org>
3  * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB. If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 #include "config.h"
23 #include <wtf/ThreadSpecific.h>
24 
25 #if !USE(PTHREADS)
26 
27 namespace WTF {
28 
29 long& flsKeyCount()
30 {
31  static long count;
32  return count;
33 }
34 
35 DWORD* flsKeys()
36 {
37  static DWORD keys[maxFlsKeySize];
38  return keys;
39 }
40 
41 } // namespace WTF
42 
43 #endif // !USE(PTHREADS)

Source/WTF/wtf/win/ThreadingWin.cpp

@@void Thread::establishPlatformSpecificHandle(HANDLE handle, ThreadIdentifier thr
265265
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);
280271}
281272
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 }
298 
299273Thread& Thread::initializeTLS(Ref<Thread>&& thread)
300274{
301275 ASSERT(s_key != InvalidThreadSpecificKey);

@@Thread& Thread::initializeTLS(Ref<Thread>&& thread)
304278 // We leak the ref to keep the Thread alive while it is held in TLS. destructTLS will deref it later at thread destruction time.
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}
313283

@@void Thread::destructTLS(void* data)
319289 Thread* thread = static_cast<Thread*>(data);
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  {
329  }
 292 thread->specificStorage().destroySlots();
 293 thread->didExit();
 294 thread->deref();
330295
331  ~ThreadExitCallback()
332  {
333  Thread::destructTLS(m_thread);
334  }
 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}
335301
336  private:
337  Thread* m_thread;
338  } callback(thread);
 302Atomic<int> Thread::SpecificStorage::s_numberOfKeys;
 303std::array<Atomic<Thread::SpecificStorage::DestroyFunction>, Thread::SpecificStorage::s_maxKeys> Thread::SpecificStorage::s_destroyFunctions;
339304
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();
 305Thread::SpecificStorage::SpecificStorage()
 306{
 307 m_slots.fill(nullptr);
 308}
348309
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;
 310bool Thread::SpecificStorage::allocKey(int& key, DestroyFunction destroy)
 311{
 312 int k = s_numberOfKeys.exchangeAdd(1);
 313 if (k >= s_maxKeys) {
 314 s_numberOfKeys.exchangeSub(1);
 315 return false;
 316 }
 317 key = k;
 318 s_destroyFunctions[key].store(destroy);
 319 return true;
 320}
 321
 322void* Thread::SpecificStorage::get(int key)
 323{
 324 return m_slots[key];
 325}
 326
 327void Thread::SpecificStorage::set(int key, void* value)
 328{
 329 m_slots[key] = value;
 330}
 331
 332void Thread::SpecificStorage::destroySlots()
 333{
 334 auto numberOfKeys = s_numberOfKeys.load();
 335 for (size_t i = 0; i < numberOfKeys; i++) {
 336 auto destroy = s_destroyFunctions[i].load();
 337 if (destroy && m_slots[i]) {
 338 destroy(m_slots[i]);
 339 m_slots[i] = nullptr;
 340 }
354341 }
355  threadSpecificSet(s_key, InvalidThread);
356  thread->m_isDestroyedOnce = true;
357342}
358343
359344Mutex::~Mutex()