tsan_dense_alloc.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. //===-- tsan_dense_alloc.h --------------------------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file is a part of ThreadSanitizer (TSan), a race detector.
  10. //
  11. // A DenseSlabAlloc is a freelist-based allocator of fixed-size objects.
  12. // DenseSlabAllocCache is a thread-local cache for DenseSlabAlloc.
  13. // The only difference with traditional slab allocators is that DenseSlabAlloc
  14. // allocates/free indices of objects and provide a functionality to map
  15. // the index onto the real pointer. The index is u32, that is, 2 times smaller
  16. // than uptr (hense the Dense prefix).
  17. //===----------------------------------------------------------------------===//
  18. #ifndef TSAN_DENSE_ALLOC_H
  19. #define TSAN_DENSE_ALLOC_H
  20. #include "sanitizer_common/sanitizer_common.h"
  21. #include "tsan_defs.h"
  22. namespace __tsan {
  23. class DenseSlabAllocCache {
  24. static const uptr kSize = 128;
  25. typedef u32 IndexT;
  26. uptr pos;
  27. IndexT cache[kSize];
  28. template <typename, uptr, uptr, u64>
  29. friend class DenseSlabAlloc;
  30. };
  31. template <typename T, uptr kL1Size, uptr kL2Size, u64 kReserved = 0>
  32. class DenseSlabAlloc {
  33. public:
  34. typedef DenseSlabAllocCache Cache;
  35. typedef typename Cache::IndexT IndexT;
  36. static_assert((kL1Size & (kL1Size - 1)) == 0,
  37. "kL1Size must be a power-of-two");
  38. static_assert((kL2Size & (kL2Size - 1)) == 0,
  39. "kL2Size must be a power-of-two");
  40. static_assert((kL1Size * kL2Size) <= (1ull << (sizeof(IndexT) * 8)),
  41. "kL1Size/kL2Size are too large");
  42. static_assert(((kL1Size * kL2Size - 1) & kReserved) == 0,
  43. "reserved bits don't fit");
  44. static_assert(sizeof(T) > sizeof(IndexT),
  45. "it doesn't make sense to use dense alloc");
  46. DenseSlabAlloc(LinkerInitialized, const char *name) : name_(name) {}
  47. explicit DenseSlabAlloc(const char *name)
  48. : DenseSlabAlloc(LINKER_INITIALIZED, name) {
  49. // It can be very large.
  50. // Don't page it in for linker initialized objects.
  51. internal_memset(map_, 0, sizeof(map_));
  52. }
  53. ~DenseSlabAlloc() {
  54. for (uptr i = 0; i < kL1Size; i++) {
  55. if (map_[i] != 0)
  56. UnmapOrDie(map_[i], kL2Size * sizeof(T));
  57. }
  58. }
  59. IndexT Alloc(Cache *c) {
  60. if (c->pos == 0)
  61. Refill(c);
  62. return c->cache[--c->pos];
  63. }
  64. void Free(Cache *c, IndexT idx) {
  65. DCHECK_NE(idx, 0);
  66. if (c->pos == Cache::kSize)
  67. Drain(c);
  68. c->cache[c->pos++] = idx;
  69. }
  70. T *Map(IndexT idx) {
  71. DCHECK_NE(idx, 0);
  72. DCHECK_LE(idx, kL1Size * kL2Size);
  73. return &map_[idx / kL2Size][idx % kL2Size];
  74. }
  75. void FlushCache(Cache *c) {
  76. while (c->pos) Drain(c);
  77. }
  78. void InitCache(Cache *c) {
  79. c->pos = 0;
  80. internal_memset(c->cache, 0, sizeof(c->cache));
  81. }
  82. uptr AllocatedMemory() const {
  83. return atomic_load_relaxed(&fillpos_) * kL2Size * sizeof(T);
  84. }
  85. template <typename Func>
  86. void ForEach(Func func) {
  87. Lock lock(&mtx_);
  88. uptr fillpos = atomic_load_relaxed(&fillpos_);
  89. for (uptr l1 = 0; l1 < fillpos; l1++) {
  90. for (IndexT l2 = l1 == 0 ? 1 : 0; l2 < kL2Size; l2++) func(&map_[l1][l2]);
  91. }
  92. }
  93. private:
  94. T *map_[kL1Size];
  95. Mutex mtx_;
  96. // The freelist is organized as a lock-free stack of batches of nodes.
  97. // The stack itself uses Block::next links, while the batch within each
  98. // stack node uses Block::batch links.
  99. // Low 32-bits of freelist_ is the node index, top 32-bits is ABA-counter.
  100. atomic_uint64_t freelist_ = {0};
  101. atomic_uintptr_t fillpos_ = {0};
  102. const char *const name_;
  103. struct Block {
  104. IndexT next;
  105. IndexT batch;
  106. };
  107. Block *MapBlock(IndexT idx) { return reinterpret_cast<Block *>(Map(idx)); }
  108. static constexpr u64 kCounterInc = 1ull << 32;
  109. static constexpr u64 kCounterMask = ~(kCounterInc - 1);
  110. NOINLINE void Refill(Cache *c) {
  111. // Pop 1 batch of nodes from the freelist.
  112. IndexT idx;
  113. u64 xchg;
  114. u64 cmp = atomic_load(&freelist_, memory_order_acquire);
  115. do {
  116. idx = static_cast<IndexT>(cmp);
  117. if (!idx)
  118. return AllocSuperBlock(c);
  119. Block *ptr = MapBlock(idx);
  120. xchg = ptr->next | (cmp & kCounterMask);
  121. } while (!atomic_compare_exchange_weak(&freelist_, &cmp, xchg,
  122. memory_order_acq_rel));
  123. // Unpack it into c->cache.
  124. while (idx) {
  125. c->cache[c->pos++] = idx;
  126. idx = MapBlock(idx)->batch;
  127. }
  128. }
  129. NOINLINE void Drain(Cache *c) {
  130. // Build a batch of at most Cache::kSize / 2 nodes linked by Block::batch.
  131. IndexT head_idx = 0;
  132. for (uptr i = 0; i < Cache::kSize / 2 && c->pos; i++) {
  133. IndexT idx = c->cache[--c->pos];
  134. Block *ptr = MapBlock(idx);
  135. ptr->batch = head_idx;
  136. head_idx = idx;
  137. }
  138. // Push it onto the freelist stack.
  139. Block *head = MapBlock(head_idx);
  140. u64 xchg;
  141. u64 cmp = atomic_load(&freelist_, memory_order_acquire);
  142. do {
  143. head->next = static_cast<IndexT>(cmp);
  144. xchg = head_idx | (cmp & kCounterMask) + kCounterInc;
  145. } while (!atomic_compare_exchange_weak(&freelist_, &cmp, xchg,
  146. memory_order_acq_rel));
  147. }
  148. NOINLINE void AllocSuperBlock(Cache *c) {
  149. Lock lock(&mtx_);
  150. uptr fillpos = atomic_load_relaxed(&fillpos_);
  151. if (fillpos == kL1Size) {
  152. Printf("ThreadSanitizer: %s overflow (%zu*%zu). Dying.\n", name_, kL1Size,
  153. kL2Size);
  154. Die();
  155. }
  156. VPrintf(2, "ThreadSanitizer: growing %s: %zu out of %zu*%zu\n", name_,
  157. fillpos, kL1Size, kL2Size);
  158. T *batch = (T *)MmapOrDie(kL2Size * sizeof(T), name_);
  159. map_[fillpos] = batch;
  160. // Reserve 0 as invalid index.
  161. for (IndexT i = fillpos ? 0 : 1; i < kL2Size; i++) {
  162. new (batch + i) T;
  163. c->cache[c->pos++] = i + fillpos * kL2Size;
  164. if (c->pos == Cache::kSize)
  165. Drain(c);
  166. }
  167. atomic_store_relaxed(&fillpos_, fillpos + 1);
  168. CHECK(c->pos);
  169. }
  170. };
  171. } // namespace __tsan
  172. #endif // TSAN_DENSE_ALLOC_H