proxygen
folly::threadlocal_detail::StaticMetaBase Struct Reference

#include <ThreadLocalDetail.h>

Inheritance diagram for folly::threadlocal_detail::StaticMetaBase:
folly::threadlocal_detail::StaticMeta< Tag, AccessMode >

Classes

class  EntryID
 

Public Member Functions

 StaticMetaBase (ThreadEntry *(*threadEntry)(), bool strict)
 
void push_back (ThreadEntry *t)
 
void erase (ThreadEntry *t)
 
uint32_t elementsCapacity () const
 
uint32_t allocate (EntryID *ent)
 
void destroy (EntryID *ent)
 
void reserve (EntryID *id)
 
ElementWrappergetElement (EntryID *ent)
 
void reserveHeadUnlocked (uint32_t id)
 
void pushBackLocked (ThreadEntry *t, uint32_t id)
 
void pushBackUnlocked (ThreadEntry *t, uint32_t id)
 

Static Public Member Functions

static FOLLY_EXPORT ThreadEntryListgetThreadEntryList ()
 
static void onThreadExit (void *ptr)
 
static ElementWrapperreallocate (ThreadEntry *threadEntry, uint32_t idval, size_t &newCapacity)
 

Public Attributes

uint32_t nextId_
 
std::vector< uint32_tfreeIds_
 
std::mutex lock_
 
SharedMutex accessAllThreadsLock_
 
pthread_key_t pthreadKey_
 
ThreadEntry head_
 
ThreadEntry *(* threadEntry_ )()
 
bool strict_
 

Protected Member Functions

 ~StaticMetaBase ()
 

Detailed Description

Definition at line 299 of file ThreadLocalDetail.h.

Constructor & Destructor Documentation

folly::threadlocal_detail::StaticMetaBase::StaticMetaBase ( ThreadEntry *(*)()  threadEntry,
bool  strict 
)

Definition at line 67 of file ThreadLocalDetail.cpp.

References folly::checkPosixError(), head_, folly::threadlocal_detail::ThreadEntry::next, onThreadExit(), folly::threadlocal_detail::ThreadEntry::prev, pthreadKey_, and folly::threadlocal_detail::PthreadKeyUnregister::registerKey().

folly::threadlocal_detail::StaticMetaBase::~StaticMetaBase ( )
inlineprotected

Definition at line 403 of file ThreadLocalDetail.h.

403 {}

Member Function Documentation

uint32_t folly::threadlocal_detail::StaticMetaBase::allocate ( EntryID ent)

Definition at line 217 of file ThreadLocalDetail.cpp.

References g(), folly::threadlocal_detail::kEntryIDInvalid, reserveHeadUnlocked(), uint32_t, and folly::threadlocal_detail::StaticMetaBase::EntryID::value.

Referenced by folly::threadlocal_detail::StaticMetaBase::EntryID::getOrAllocate().

217  {
218  uint32_t id;
219  auto& meta = *this;
220  std::lock_guard<std::mutex> g(meta.lock_);
221 
222  id = ent->value.load();
223  if (id != kEntryIDInvalid) {
224  return id;
225  }
226 
227  if (!meta.freeIds_.empty()) {
228  id = meta.freeIds_.back();
229  meta.freeIds_.pop_back();
230  } else {
231  id = meta.nextId_++;
232  }
233 
234  uint32_t old_id = ent->value.exchange(id);
235  DCHECK_EQ(old_id, kEntryIDInvalid);
236 
238 
239  return id;
240 }
constexpr uint32_t kEntryIDInvalid
g_t g(f_t)
void folly::threadlocal_detail::StaticMetaBase::destroy ( EntryID ent)

Definition at line 242 of file ThreadLocalDetail.cpp.

References folly::ALL_THREADS, folly::threadlocal_detail::ElementWrapper::deleter1, folly::threadlocal_detail::ThreadEntry::elements, elementsCapacity(), g(), folly::threadlocal_detail::ThreadEntry::getElementsCapacity(), folly::threadlocal_detail::kEntryIDInvalid, cpp.ast::next(), folly::threadlocal_detail::ElementWrapper::ownsDeleter, ptr, folly::threadlocal_detail::ElementWrapper::ptr, uint32_t, folly::threadlocal_detail::StaticMetaBase::EntryID::value, folly::WARNING, and folly::wlock().

242  {
243  try {
244  auto& meta = *this;
245 
246  // Elements in other threads that use this id.
247  std::vector<ElementWrapper> elements;
248 
249  {
250  SharedMutex::WriteHolder wlock(nullptr);
251  if (meta.strict_) {
252  /*
253  * In strict mode, the logic guarantees per-thread instances are
254  * destroyed by the moment ThreadLocal<> dtor returns.
255  * In order to achieve that, we should wait until concurrent
256  * onThreadExit() calls (that might acquire ownership over per-thread
257  * instances in order to destroy them) are finished.
258  */
259  wlock = SharedMutex::WriteHolder(meta.accessAllThreadsLock_);
260  }
261 
262  {
263  std::lock_guard<std::mutex> g(meta.lock_);
264  uint32_t id = ent->value.exchange(kEntryIDInvalid);
265  if (id == kEntryIDInvalid) {
266  return;
267  }
268 
269  auto& node = meta.head_.elements[id].node;
270  while (!node.empty()) {
271  auto* next = node.getNext();
272  next->eraseZero();
273 
274  ThreadEntry* e = next->parent;
275  auto elementsCapacity = e->getElementsCapacity();
276  if (id < elementsCapacity && e->elements[id].ptr) {
277  elements.push_back(e->elements[id]);
278 
279  /*
280  * Writing another thread's ThreadEntry from here is fine;
281  * the only other potential reader is the owning thread --
282  * from onThreadExit (which grabs the lock, so is properly
283  * synchronized with us) or from get(), which also grabs
284  * the lock if it needs to resize the elements vector.
285  *
286  * We can't conflict with reads for a get(id), because
287  * it's illegal to call get on a thread local that's
288  * destructing.
289  */
290  e->elements[id].ptr = nullptr;
291  e->elements[id].deleter1 = nullptr;
292  e->elements[id].ownsDeleter = false;
293  }
294  }
295  meta.freeIds_.push_back(id);
296  }
297  }
298  // Delete elements outside the locks.
299  for (ElementWrapper& elem : elements) {
300  if (elem.dispose(TLPDestructionMode::ALL_THREADS)) {
301  elem.cleanup();
302  }
303  }
304  } catch (...) { // Just in case we get a lock error or something anyway...
305  LOG(WARNING) << "Destructor discarding an exception that was thrown.";
306  }
307 }
void * ptr
auto wlock(Synchronized< D, M > &synchronized, Args &&...args)
constexpr uint32_t kEntryIDInvalid
g_t g(f_t)
def next(obj)
Definition: ast.py:58
uint32_t folly::threadlocal_detail::StaticMetaBase::elementsCapacity ( ) const

Definition at line 211 of file ThreadLocalDetail.cpp.

References FOLLY_LIKELY, and folly::threadlocal_detail::ThreadEntry::getElementsCapacity().

Referenced by destroy(), and onThreadExit().

211  {
212  ThreadEntry* threadEntry = (*threadEntry_)();
213 
214  return FOLLY_LIKELY(!!threadEntry) ? threadEntry->getElementsCapacity() : 0;
215 }
#define FOLLY_LIKELY(x)
Definition: Likely.h:35
void folly::threadlocal_detail::StaticMetaBase::erase ( ThreadEntry t)
inline

Definition at line 351 of file ThreadLocalDetail.h.

References destroy(), FOLLY_EXPORT, folly::threadlocal_detail::ThreadEntry::next, folly::threadlocal_detail::ThreadEntry::prev, ptr, folly::reserve, folly::pushmi::detail::t, and uint32_t.

351  {
352  t->next->prev = t->prev;
353  t->prev->next = t->next;
354  t->next = t->prev = t;
355  }
ElementWrapper& folly::threadlocal_detail::StaticMetaBase::getElement ( EntryID ent)
ThreadEntryList * folly::threadlocal_detail::StaticMetaBase::getThreadEntryList ( )
static

Definition at line 75 of file ThreadLocalDetail.cpp.

References folly::checkPosixError(), FOLLY_ALWAYS_INLINE, pthreadKey_, folly::threadlocal_detail::PthreadKeyUnregister::registerKey(), and UNLIKELY.

75  {
76 #ifdef FOLLY_TLD_USE_FOLLY_TLS
77  static FOLLY_TLS ThreadEntryList threadEntryListSingleton;
78  return &threadEntryListSingleton;
79 #else
80  class PthreadKey {
81  public:
82  PthreadKey() {
83  int ret = pthread_key_create(&pthreadKey_, nullptr);
84  checkPosixError(ret, "pthread_key_create failed");
86  }
87 
88  FOLLY_ALWAYS_INLINE pthread_key_t get() const {
89  return pthreadKey_;
90  }
91 
92  private:
93  pthread_key_t pthreadKey_;
94  };
95 
96  static auto instance = detail::createGlobal<PthreadKey, void>();
97 
98  ThreadEntryList* threadEntryList =
99  static_cast<ThreadEntryList*>(pthread_getspecific(instance->get()));
100 
101  if (UNLIKELY(!threadEntryList)) {
102  threadEntryList = new ThreadEntryList();
103  int ret = pthread_setspecific(instance->get(), threadEntryList);
104  checkPosixError(ret, "pthread_setspecific failed");
105  }
106 
107  return threadEntryList;
108 #endif
109 }
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
void checkPosixError(int err, Args &&...args)
Definition: Exception.h:83
#define UNLIKELY(x)
Definition: Likely.h:48
void folly::threadlocal_detail::StaticMetaBase::onThreadExit ( void *  ptr)
static

Definition at line 111 of file ThreadLocalDetail.cpp.

References elementsCapacity(), FOR_EACH_RANGE, bm::free(), g(), i, folly::threadlocal_detail::ThreadEntry::meta, ptr, folly::rlock(), and folly::THIS_THREAD.

Referenced by StaticMetaBase().

111  {
112  auto threadEntry = static_cast<ThreadEntry*>(ptr);
113 
114  {
115  auto& meta = *threadEntry->meta;
116 
117  // Make sure this ThreadEntry is available if ThreadLocal A is accessed in
118  // ThreadLocal B destructor.
119  pthread_setspecific(meta.pthreadKey_, threadEntry);
120  SharedMutex::ReadHolder rlock(nullptr);
121  if (meta.strict_) {
122  rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_);
123  }
124  {
125  std::lock_guard<std::mutex> g(meta.lock_);
126  // mark it as removed
127  threadEntry->removed_ = true;
128  meta.erase(&(*threadEntry));
129  auto elementsCapacity = threadEntry->getElementsCapacity();
130  for (size_t i = 0u; i < elementsCapacity; ++i) {
131  threadEntry->elements[i].node.eraseZero();
132  }
133  // No need to hold the lock any longer; the ThreadEntry is private to this
134  // thread now that it's been removed from meta.
135  }
136  // NOTE: User-provided deleter / object dtor itself may be using ThreadLocal
137  // with the same Tag, so dispose() calls below may (re)create some of the
138  // elements or even increase elementsCapacity, thus multiple cleanup rounds
139  // may be required.
140  for (bool shouldRun = true; shouldRun;) {
141  shouldRun = false;
142  auto elementsCapacity = threadEntry->getElementsCapacity();
143  FOR_EACH_RANGE (i, 0, elementsCapacity) {
144  if (threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {
145  threadEntry->elements[i].cleanup();
146  shouldRun = true;
147  }
148  }
149  }
150  pthread_setspecific(meta.pthreadKey_, nullptr);
151  }
152 
153  auto threadEntryList = threadEntry->list;
154  DCHECK_GT(threadEntryList->count, 0u);
155 
156  --threadEntryList->count;
157 
158  if (threadEntryList->count) {
159  return;
160  }
161 
162  // dispose all the elements
163  for (bool shouldRunOuter = true; shouldRunOuter;) {
164  shouldRunOuter = false;
165  auto tmp = threadEntryList->head;
166  while (tmp) {
167  auto& meta = *tmp->meta;
168  pthread_setspecific(meta.pthreadKey_, tmp);
169  SharedMutex::ReadHolder rlock(nullptr);
170  if (meta.strict_) {
171  rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_);
172  }
173  for (bool shouldRunInner = true; shouldRunInner;) {
174  shouldRunInner = false;
175  auto elementsCapacity = tmp->getElementsCapacity();
176  FOR_EACH_RANGE (i, 0, elementsCapacity) {
177  if (tmp->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {
178  tmp->elements[i].cleanup();
179  shouldRunInner = true;
180  shouldRunOuter = true;
181  }
182  }
183  }
184  pthread_setspecific(meta.pthreadKey_, nullptr);
185  tmp = tmp->listNext;
186  }
187  }
188 
189  // free the entry list
190  auto head = threadEntryList->head;
191  threadEntryList->head = nullptr;
192  while (head) {
193  auto tmp = head;
194  head = head->listNext;
195  if (tmp->elements) {
196  free(tmp->elements);
197  tmp->elements = nullptr;
198  tmp->setElementsCapacity(0);
199  }
200 
201 #ifndef FOLLY_TLD_USE_FOLLY_TLS
202  delete tmp;
203 #endif
204  }
205 
206 #ifndef FOLLY_TLD_USE_FOLLY_TLS
207  delete threadEntryList;
208 #endif
209 }
void * ptr
auto rlock(const Synchronized< Data, Mutex > &synchronized, Args &&...args)
#define FOR_EACH_RANGE(i, begin, end)
Definition: Foreach.h:313
void free()
g_t g(f_t)
void folly::threadlocal_detail::StaticMetaBase::push_back ( ThreadEntry t)
inline
void folly::threadlocal_detail::StaticMetaBase::pushBackLocked ( ThreadEntry t,
uint32_t  id 
)

Definition at line 452 of file ThreadLocalDetail.cpp.

References folly::threadlocal_detail::ThreadEntry::elements, g(), head_, LIKELY, lock_, folly::threadlocal_detail::ElementWrapper::node, folly::threadlocal_detail::ThreadEntryNode::push_back(), and folly::threadlocal_detail::ThreadEntry::removed_.

Referenced by folly::threadlocal_detail::ThreadEntryNode::initIfZero().

452  {
453  if (LIKELY(!t->removed_)) {
454  std::lock_guard<std::mutex> g(lock_);
455  auto* node = &t->elements[id].node;
456  node->push_back(&head_);
457  }
458 }
#define LIKELY(x)
Definition: Likely.h:47
g_t g(f_t)
void folly::threadlocal_detail::StaticMetaBase::pushBackUnlocked ( ThreadEntry t,
uint32_t  id 
)
ElementWrapper * folly::threadlocal_detail::StaticMetaBase::reallocate ( ThreadEntry threadEntry,
uint32_t  idval,
size_t &  newCapacity 
)
static

Definition at line 309 of file ThreadLocalDetail.cpp.

References folly::threadlocal_detail::ThreadEntry::elements, folly::threadlocal_detail::ThreadEntry::getElementsCapacity(), head_, folly::jemallocMinInPlaceExpandable, kBigGrowthFactor, kSmallGrowthFactor, mallocx, MALLOCX_ZERO, folly::threadlocal_detail::ThreadEntry::meta, nallocx, folly::detail::success, folly::usingJEMalloc(), and xallocx.

Referenced by reserve(), and reserveHeadUnlocked().

312  {
313  size_t prevCapacity = threadEntry->getElementsCapacity();
314 
315  // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent
316  // very slow start.
317  auto smallCapacity = static_cast<size_t>((idval + 5) * kSmallGrowthFactor);
318  auto bigCapacity = static_cast<size_t>((idval + 5) * kBigGrowthFactor);
319 
320  newCapacity =
321  (threadEntry->meta &&
322  (bigCapacity <= threadEntry->meta->head_.getElementsCapacity()))
323  ? bigCapacity
324  : smallCapacity;
325 
326  assert(newCapacity > prevCapacity);
327  ElementWrapper* reallocated = nullptr;
328 
329  // Need to grow. Note that we can't call realloc, as elements is
330  // still linked in meta, so another thread might access invalid memory
331  // after realloc succeeds. We'll copy by hand and update our ThreadEntry
332  // under the lock.
333  if (usingJEMalloc()) {
334  bool success = false;
335  size_t newByteSize = nallocx(newCapacity * sizeof(ElementWrapper), 0);
336 
337  // Try to grow in place.
338  //
339  // Note that xallocx(MALLOCX_ZERO) will only zero newly allocated memory,
340  // even if a previous allocation allocated more than we requested.
341  // This is fine; we always use MALLOCX_ZERO with jemalloc and we
342  // always expand our allocation to the real size.
343  if (prevCapacity * sizeof(ElementWrapper) >= jemallocMinInPlaceExpandable) {
344  success =
345  (xallocx(threadEntry->elements, newByteSize, 0, MALLOCX_ZERO) ==
346  newByteSize);
347  }
348 
349  // In-place growth failed.
350  if (!success) {
351  success =
352  ((reallocated = static_cast<ElementWrapper*>(
353  mallocx(newByteSize, MALLOCX_ZERO))) != nullptr);
354  }
355 
356  if (success) {
357  // Expand to real size
358  assert(newByteSize / sizeof(ElementWrapper) >= newCapacity);
359  newCapacity = newByteSize / sizeof(ElementWrapper);
360  } else {
361  throw std::bad_alloc();
362  }
363  } else { // no jemalloc
364  // calloc() is simpler than malloc() followed by memset(), and
365  // potentially faster when dealing with a lot of memory, as it can get
366  // already-zeroed pages from the kernel.
367  reallocated = static_cast<ElementWrapper*>(
368  calloc(newCapacity, sizeof(ElementWrapper)));
369  if (!reallocated) {
370  throw std::bad_alloc();
371  }
372  }
373 
374  return reallocated;
375 }
constexpr auto kBigGrowthFactor
bool usingJEMalloc() noexcept
Definition: Malloc.h:147
#define MALLOCX_ZERO
Definition: Malloc.h:38
static const size_t jemallocMinInPlaceExpandable
Definition: Malloc.h:221
size_t(* xallocx)(void *, size_t, size_t, int)
Definition: MallocImpl.cpp:37
size_t(* nallocx)(size_t, int)
Definition: MallocImpl.cpp:41
void *(* mallocx)(size_t, int)
Definition: MallocImpl.cpp:35
constexpr auto kSmallGrowthFactor
void folly::threadlocal_detail::StaticMetaBase::reserve ( EntryID id)

Reserve enough space in the ThreadEntry::elements for the item to fit in.

Definition at line 382 of file ThreadLocalDetail.cpp.

References folly::threadlocal_detail::ThreadEntry::elements, bm::free(), g(), folly::threadlocal_detail::ThreadEntry::getElementsCapacity(), i, folly::threadlocal_detail::ThreadEntryNode::initZero(), folly::threadlocal_detail::ElementWrapper::node, reallocate(), folly::threadlocal_detail::ThreadEntry::setElementsCapacity(), folly::f14::swap(), and uint32_t.

382  {
383  auto& meta = *this;
384  ThreadEntry* threadEntry = (*threadEntry_)();
385  size_t prevCapacity = threadEntry->getElementsCapacity();
386 
387  uint32_t idval = id->getOrAllocate(meta);
388  if (prevCapacity > idval) {
389  return;
390  }
391 
392  size_t newCapacity;
393  ElementWrapper* reallocated = reallocate(threadEntry, idval, newCapacity);
394 
395  // Success, update the entry
396  {
397  std::lock_guard<std::mutex> g(meta.lock_);
398 
399  if (prevCapacity == 0) {
400  meta.push_back(threadEntry);
401  }
402 
403  if (reallocated) {
404  /*
405  * Note: we need to hold the meta lock when copying data out of
406  * the old vector, because some other thread might be
407  * destructing a ThreadLocal and writing to the elements vector
408  * of this thread.
409  */
410  if (prevCapacity != 0) {
411  memcpy(
412  reallocated,
413  threadEntry->elements,
414  sizeof(*reallocated) * prevCapacity);
415  }
416  std::swap(reallocated, threadEntry->elements);
417  }
418 
419  for (size_t i = prevCapacity; i < newCapacity; i++) {
420  threadEntry->elements[i].node.initZero(threadEntry, i);
421  }
422 
423  threadEntry->setElementsCapacity(newCapacity);
424  }
425 
426  free(reallocated);
427 }
static ElementWrapper * reallocate(ThreadEntry *threadEntry, uint32_t idval, size_t &newCapacity)
void free()
g_t g(f_t)
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
void folly::threadlocal_detail::StaticMetaBase::reserveHeadUnlocked ( uint32_t  id)

Definition at line 429 of file ThreadLocalDetail.cpp.

References folly::threadlocal_detail::ThreadEntry::elements, bm::free(), folly::threadlocal_detail::ThreadEntry::getElementsCapacity(), head_, i, folly::threadlocal_detail::ThreadEntryNode::init(), folly::threadlocal_detail::ElementWrapper::node, reallocate(), folly::threadlocal_detail::ThreadEntry::setElementsCapacity(), and folly::f14::swap().

Referenced by allocate().

429  {
430  if (head_.getElementsCapacity() <= id) {
431  size_t prevCapacity = head_.getElementsCapacity();
432  size_t newCapacity;
433  ElementWrapper* reallocated = reallocate(&head_, id, newCapacity);
434 
435  if (reallocated) {
436  if (prevCapacity != 0) {
437  memcpy(
438  reallocated, head_.elements, sizeof(*reallocated) * prevCapacity);
439  }
440  std::swap(reallocated, head_.elements);
441  }
442 
443  for (size_t i = prevCapacity; i < newCapacity; i++) {
445  }
446 
447  head_.setElementsCapacity(newCapacity);
448  free(reallocated);
449  }
450 }
static ElementWrapper * reallocate(ThreadEntry *threadEntry, uint32_t idval, size_t &newCapacity)
void init(ThreadEntry *entry, uint32_t newId)
size_t getElementsCapacity() const noexcept
void free()
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
void setElementsCapacity(size_t capacity) noexcept

Member Data Documentation

SharedMutex folly::threadlocal_detail::StaticMetaBase::accessAllThreadsLock_

Definition at line 396 of file ThreadLocalDetail.h.

std::vector<uint32_t> folly::threadlocal_detail::StaticMetaBase::freeIds_

Definition at line 394 of file ThreadLocalDetail.h.

std::mutex folly::threadlocal_detail::StaticMetaBase::lock_

Definition at line 395 of file ThreadLocalDetail.h.

Referenced by pushBackLocked().

uint32_t folly::threadlocal_detail::StaticMetaBase::nextId_

Definition at line 393 of file ThreadLocalDetail.h.

pthread_key_t folly::threadlocal_detail::StaticMetaBase::pthreadKey_

Definition at line 397 of file ThreadLocalDetail.h.

Referenced by getThreadEntryList(), and StaticMetaBase().

bool folly::threadlocal_detail::StaticMetaBase::strict_

Definition at line 400 of file ThreadLocalDetail.h.

ThreadEntry*(* folly::threadlocal_detail::StaticMetaBase::threadEntry_) ()

Definition at line 399 of file ThreadLocalDetail.h.


The documentation for this struct was generated from the following files: