free_list-inl.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #ifndef FREE_LIST_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include free_list.h"
  3. // For the sake of sane code completion.
  4. #include "free_list.h"
  5. #endif
  6. namespace NYT {
  7. ////////////////////////////////////////////////////////////////////////////////
  8. // DCAS is supported in Clang with option -mcx16, is not supported in GCC. See following links.
  9. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84522
  10. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878
  11. template <class T1, class T2>
  12. Y_FORCE_INLINE bool CompareAndSet(
  13. TAtomicUint128* atomic,
  14. T1& expected1,
  15. T2& expected2,
  16. T1 new1,
  17. T2 new2)
  18. {
  19. #if defined(__x86_64__)
  20. bool success;
  21. __asm__ __volatile__
  22. (
  23. "lock cmpxchg16b %1\n"
  24. "setz %0"
  25. : "=q"(success)
  26. , "+m"(*atomic)
  27. , "+a"(expected1)
  28. , "+d"(expected2)
  29. : "b"(new1)
  30. , "c"(new2)
  31. : "cc"
  32. );
  33. return success;
  34. #elif defined(__arm64__) || (defined(__aarch64__) && defined(RTE_ARM_FEATURE_ATOMICS))
  35. register ui64 x0 __asm("x0") = (ui64)expected1;
  36. register ui64 x1 __asm("x1") = (ui64)expected2;
  37. register ui64 x2 __asm("x2") = (ui64)new1;
  38. register ui64 x3 __asm("x3") = (ui64)new2;
  39. ui64 old1 = (ui64)expected1;
  40. ui64 old2 = (ui64)expected2;
  41. asm volatile
  42. (
  43. #if defined(RTE_CC_CLANG)
  44. ".arch armv8-a+lse\n"
  45. #endif
  46. "caspal %[old0], %[old1], %[upd0], %[upd1], [%[dst]]"
  47. : [old0] "+r" (x0)
  48. , [old1] "+r" (x1)
  49. : [upd0] "r" (x2)
  50. , [upd1] "r" (x3)
  51. , [dst] "r" (atomic)
  52. : "memory"
  53. );
  54. expected1 = (T1)x0;
  55. expected2 = (T2)x1;
  56. return x0 == old1 && x1 == old2;
  57. #elif defined(__aarch64__)
  58. ui64 exp1 = reinterpret_cast<ui64>(expected1);
  59. ui64 exp2 = reinterpret_cast<ui64>(expected2);
  60. ui32 fail = 0;
  61. do {
  62. ui64 current1 = 0;
  63. ui64 current2 = 0;
  64. asm volatile (
  65. "ldaxp %[cur1], %[cur2], [%[src]]"
  66. : [cur1] "=r" (current1)
  67. , [cur2] "=r" (current2)
  68. : [src] "r" (atomic)
  69. : "memory"
  70. );
  71. if (current1 != exp1 || current2 != exp2) {
  72. expected1 = reinterpret_cast<T1>(current1);
  73. expected2 = reinterpret_cast<T2>(current2);
  74. return false;
  75. }
  76. asm volatile (
  77. "stlxp %w[fail], %[new1], %[new2], [%[dst]]"
  78. : [fail] "=&r" (fail)
  79. : [new1] "r" (new1)
  80. , [new2] "r" (new2)
  81. , [dst] "r" (atomic)
  82. : "memory"
  83. );
  84. } while (Y_UNLIKELY(fail));
  85. return true;
  86. #else
  87. # error Unsupported platform
  88. #endif
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////
  91. template <class TItem>
  92. TFreeList<TItem>::THead::THead(TItem* pointer)
  93. : Pointer(pointer)
  94. { }
  95. template <class TItem>
  96. TFreeList<TItem>::TFreeList()
  97. : Head_()
  98. { }
  99. template <class TItem>
  100. TFreeList<TItem>::TFreeList(TFreeList<TItem>&& other)
  101. : Head_(other.ExtractAll())
  102. { }
  103. template <class TItem>
  104. TFreeList<TItem>::~TFreeList()
  105. {
  106. YT_VERIFY(IsEmpty());
  107. }
  108. template <class TItem>
  109. template <class TPredicate>
  110. Y_NO_SANITIZE("thread")
  111. bool TFreeList<TItem>::PutIf(TItem* head, TItem* tail, TPredicate predicate)
  112. {
  113. auto* current = Head_.Pointer.load(std::memory_order::relaxed);
  114. auto epoch = Head_.Epoch.load(std::memory_order::relaxed);
  115. while (predicate(current)) {
  116. tail->Next.store(current, std::memory_order::release);
  117. if (CompareAndSet(&AtomicHead_, current, epoch, head, epoch + 1)) {
  118. return true;
  119. }
  120. }
  121. tail->Next.store(nullptr, std::memory_order::release);
  122. return false;
  123. }
  124. template <class TItem>
  125. Y_NO_SANITIZE("thread")
  126. void TFreeList<TItem>::Put(TItem* head, TItem* tail)
  127. {
  128. auto* current = Head_.Pointer.load(std::memory_order::relaxed);
  129. auto epoch = Head_.Epoch.load(std::memory_order::relaxed);
  130. do {
  131. tail->Next.store(current, std::memory_order::release);
  132. } while (!CompareAndSet(&AtomicHead_, current, epoch, head, epoch + 1));
  133. }
  134. template <class TItem>
  135. void TFreeList<TItem>::Put(TItem* item)
  136. {
  137. Put(item, item);
  138. }
  139. template <class TItem>
  140. Y_NO_SANITIZE("thread")
  141. TItem* TFreeList<TItem>::Extract()
  142. {
  143. auto* current = Head_.Pointer.load(std::memory_order::relaxed);
  144. auto epoch = Head_.Epoch.load(std::memory_order::relaxed);
  145. while (current) {
  146. // If current node is already extracted by other thread
  147. // there can be any writes at address &current->Next.
  148. // The only guaranteed thing is that address is valid (memory is not freed).
  149. auto next = current->Next.load(std::memory_order::acquire);
  150. if (CompareAndSet(&AtomicHead_, current, epoch, next, epoch + 1)) {
  151. current->Next.store(nullptr, std::memory_order::release);
  152. return current;
  153. }
  154. }
  155. return nullptr;
  156. }
  157. template <class TItem>
  158. TItem* TFreeList<TItem>::ExtractAll()
  159. {
  160. auto* current = Head_.Pointer.load(std::memory_order::relaxed);
  161. auto epoch = Head_.Epoch.load(std::memory_order::relaxed);
  162. while (current) {
  163. if (CompareAndSet<TItem*, size_t>(&AtomicHead_, current, epoch, nullptr, epoch + 1)) {
  164. return current;
  165. }
  166. }
  167. return nullptr;
  168. }
  169. template <class TItem>
  170. bool TFreeList<TItem>::IsEmpty() const
  171. {
  172. return Head_.Pointer.load() == nullptr;
  173. }
  174. template <class TItem>
  175. void TFreeList<TItem>::Append(TFreeList<TItem>& other)
  176. {
  177. auto* head = other.ExtractAll();
  178. if (!head) {
  179. return;
  180. }
  181. auto* tail = head;
  182. while (tail->Next) {
  183. tail = tail->Next;
  184. }
  185. Put(head, tail);
  186. }
  187. ////////////////////////////////////////////////////////////////////////////////
  188. } // namespace NYT