reader_writer_queue.h 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. // SPDX-FileCopyrightText: 2013-2020 Cameron Desrochers
  2. // SPDX-License-Identifier: BSD-2-Clause
  3. #pragma once
  4. #include <cassert>
  5. #include <cstdint>
  6. #include <cstdlib> // For malloc/free/abort & size_t
  7. #include <memory>
  8. #include <new>
  9. #include <stdexcept>
  10. #include <type_traits>
  11. #include <utility>
  12. #include "common/atomic_helpers.h"
  13. #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
  14. #include <chrono>
  15. #endif
  16. // A lock-free queue for a single-consumer, single-producer architecture.
  17. // The queue is also wait-free in the common path (except if more memory
  18. // needs to be allocated, in which case malloc is called).
  19. // Allocates memory sparingly, and only once if the original maximum size
  20. // estimate is never exceeded.
  21. // Tested on x86/x64 processors, but semantics should be correct for all
  22. // architectures (given the right implementations in atomicops.h), provided
  23. // that aligned integer and pointer accesses are naturally atomic.
  24. // Note that there should only be one consumer thread and producer thread;
  25. // Switching roles of the threads, or using multiple consecutive threads for
  26. // one role, is not safe unless properly synchronized.
  27. // Using the queue exclusively from one thread is fine, though a bit silly.
  28. #ifndef MOODYCAMEL_CACHE_LINE_SIZE
  29. #define MOODYCAMEL_CACHE_LINE_SIZE 64
  30. #endif
  31. #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
  32. #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || \
  33. (!defined(_MSC_VER) && !defined(__GNUC__))
  34. #define MOODYCAMEL_EXCEPTIONS_ENABLED
  35. #endif
  36. #endif
  37. #ifndef MOODYCAMEL_HAS_EMPLACE
  38. #if !defined(_MSC_VER) || \
  39. _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
  40. #define MOODYCAMEL_HAS_EMPLACE 1
  41. #endif
  42. #endif
  43. #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
  44. #if defined(__APPLE__) && defined(__MACH__) && __cplusplus >= 201703L
  45. // This is required to find out what deployment target we are using
  46. #include <CoreFoundation/CoreFoundation.h>
  47. #if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
  48. MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
  49. // C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we
  50. // can't support over-alignment in this case
  51. #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
  52. #endif
  53. #endif
  54. #endif
  55. #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
  56. #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
  57. #endif
  58. #ifdef AE_VCPP
  59. #pragma warning(push)
  60. #pragma warning(disable : 4324) // structure was padded due to __declspec(align())
  61. #pragma warning(disable : 4820) // padding was added
  62. #pragma warning(disable : 4127) // conditional expression is constant
  63. #endif
  64. namespace Common {
  65. template <typename T, size_t MAX_BLOCK_SIZE = 512>
  66. class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue {
  67. // Design: Based on a queue-of-queues. The low-level queues are just
  68. // circular buffers with front and tail indices indicating where the
  69. // next element to dequeue is and where the next element can be enqueued,
  70. // respectively. Each low-level queue is called a "block". Each block
  71. // wastes exactly one element's worth of space to keep the design simple
  72. // (if front == tail then the queue is empty, and can't be full).
  73. // The high-level queue is a circular linked list of blocks; again there
  74. // is a front and tail, but this time they are pointers to the blocks.
  75. // The front block is where the next element to be dequeued is, provided
  76. // the block is not empty. The back block is where elements are to be
  77. // enqueued, provided the block is not full.
  78. // The producer thread owns all the tail indices/pointers. The consumer
  79. // thread owns all the front indices/pointers. Both threads read each
  80. // other's variables, but only the owning thread updates them. E.g. After
  81. // the consumer reads the producer's tail, the tail may change before the
  82. // consumer is done dequeuing an object, but the consumer knows the tail
  83. // will never go backwards, only forwards.
  84. // If there is no room to enqueue an object, an additional block (of
  85. // equal size to the last block) is added. Blocks are never removed.
  86. public:
  87. typedef T value_type;
  88. // Constructs a queue that can hold at least `size` elements without further
  89. // allocations. If more than MAX_BLOCK_SIZE elements are requested,
  90. // then several blocks of MAX_BLOCK_SIZE each are reserved (including
  91. // at least one extra buffer block).
  92. AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
  93. #ifndef NDEBUG
  94. : enqueuing(false), dequeuing(false)
  95. #endif
  96. {
  97. assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) &&
  98. "MAX_BLOCK_SIZE must be a power of 2");
  99. assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
  100. Block* firstBlock = nullptr;
  101. largestBlockSize =
  102. ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
  103. if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
  104. // We need a spare block in case the producer is writing to a different block the
  105. // consumer is reading from, and wants to enqueue the maximum number of elements. We
  106. // also need a spare element in each block to avoid the ambiguity between front == tail
  107. // meaning "empty" and "full". So the effective number of slots that are guaranteed to
  108. // be usable at any time is the block size - 1 times the number of blocks - 1. Solving
  109. // for size and applying a ceiling to the division gives us (after simplifying):
  110. size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
  111. largestBlockSize = MAX_BLOCK_SIZE;
  112. Block* lastBlock = nullptr;
  113. for (size_t i = 0; i != initialBlockCount; ++i) {
  114. auto block = make_block(largestBlockSize);
  115. if (block == nullptr) {
  116. #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
  117. throw std::bad_alloc();
  118. #else
  119. abort();
  120. #endif
  121. }
  122. if (firstBlock == nullptr) {
  123. firstBlock = block;
  124. } else {
  125. lastBlock->next = block;
  126. }
  127. lastBlock = block;
  128. block->next = firstBlock;
  129. }
  130. } else {
  131. firstBlock = make_block(largestBlockSize);
  132. if (firstBlock == nullptr) {
  133. #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
  134. throw std::bad_alloc();
  135. #else
  136. abort();
  137. #endif
  138. }
  139. firstBlock->next = firstBlock;
  140. }
  141. frontBlock = firstBlock;
  142. tailBlock = firstBlock;
  143. // Make sure the reader/writer threads will have the initialized memory setup above:
  144. fence(memory_order_sync);
  145. }
  146. // Note: The queue should not be accessed concurrently while it's
  147. // being moved. It's up to the user to synchronize this.
  148. AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
  149. : frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()),
  150. largestBlockSize(other.largestBlockSize)
  151. #ifndef NDEBUG
  152. ,
  153. enqueuing(false), dequeuing(false)
  154. #endif
  155. {
  156. other.largestBlockSize = 32;
  157. Block* b = other.make_block(other.largestBlockSize);
  158. if (b == nullptr) {
  159. #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
  160. throw std::bad_alloc();
  161. #else
  162. abort();
  163. #endif
  164. }
  165. b->next = b;
  166. other.frontBlock = b;
  167. other.tailBlock = b;
  168. }
  169. // Note: The queue should not be accessed concurrently while it's
  170. // being moved. It's up to the user to synchronize this.
  171. ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN {
  172. Block* b = frontBlock.load();
  173. frontBlock = other.frontBlock.load();
  174. other.frontBlock = b;
  175. b = tailBlock.load();
  176. tailBlock = other.tailBlock.load();
  177. other.tailBlock = b;
  178. std::swap(largestBlockSize, other.largestBlockSize);
  179. return *this;
  180. }
  181. // Note: The queue should not be accessed concurrently while it's
  182. // being deleted. It's up to the user to synchronize this.
  183. AE_NO_TSAN ~ReaderWriterQueue() {
  184. // Make sure we get the latest version of all variables from other CPUs:
  185. fence(memory_order_sync);
  186. // Destroy any remaining objects in queue and free memory
  187. Block* frontBlock_ = frontBlock;
  188. Block* block = frontBlock_;
  189. do {
  190. Block* nextBlock = block->next;
  191. size_t blockFront = block->front;
  192. size_t blockTail = block->tail;
  193. for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
  194. auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
  195. element->~T();
  196. (void)element;
  197. }
  198. auto rawBlock = block->rawThis;
  199. block->~Block();
  200. std::free(rawBlock);
  201. block = nextBlock;
  202. } while (block != frontBlock_);
  203. }
  204. // Enqueues a copy of element if there is room in the queue.
  205. // Returns true if the element was enqueued, false otherwise.
  206. // Does not allocate memory.
  207. AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
  208. return inner_enqueue<CannotAlloc>(element);
  209. }
  210. // Enqueues a moved copy of element if there is room in the queue.
  211. // Returns true if the element was enqueued, false otherwise.
  212. // Does not allocate memory.
  213. AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
  214. return inner_enqueue<CannotAlloc>(std::forward<T>(element));
  215. }
  216. #if MOODYCAMEL_HAS_EMPLACE
  217. // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
  218. template <typename... Args>
  219. AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
  220. return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
  221. }
  222. #endif
  223. // Enqueues a copy of element on the queue.
  224. // Allocates an additional block of memory if needed.
  225. // Only fails (returns false) if memory allocation fails.
  226. AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
  227. return inner_enqueue<CanAlloc>(element);
  228. }
  229. // Enqueues a moved copy of element on the queue.
  230. // Allocates an additional block of memory if needed.
  231. // Only fails (returns false) if memory allocation fails.
  232. AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
  233. return inner_enqueue<CanAlloc>(std::forward<T>(element));
  234. }
  235. #if MOODYCAMEL_HAS_EMPLACE
  236. // Like enqueue() but with emplace semantics (i.e. construct-in-place).
  237. template <typename... Args>
  238. AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
  239. return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
  240. }
  241. #endif
  242. // Attempts to dequeue an element; if the queue is empty,
  243. // returns false instead. If the queue has at least one element,
  244. // moves front to result using operator=, then returns true.
  245. template <typename U>
  246. bool try_dequeue(U& result) AE_NO_TSAN {
  247. #ifndef NDEBUG
  248. ReentrantGuard guard(this->dequeuing);
  249. #endif
  250. // High-level pseudocode:
  251. // Remember where the tail block is
  252. // If the front block has an element in it, dequeue it
  253. // Else
  254. // If front block was the tail block when we entered the function, return false
  255. // Else advance to next block and dequeue the item there
  256. // Note that we have to use the value of the tail block from before we check if the front
  257. // block is full or not, in case the front block is empty and then, before we check if the
  258. // tail block is at the front block or not, the producer fills up the front block *and
  259. // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
  260. // reproducible in practice.
  261. // In order to avoid overhead in the common case, though, we do a double-checked pattern
  262. // where we have the fast path if the front block is not empty, then read the tail block,
  263. // then re-read the front block and check if it's not empty again, then check if the tail
  264. // block has advanced.
  265. Block* frontBlock_ = frontBlock.load();
  266. size_t blockTail = frontBlock_->localTail;
  267. size_t blockFront = frontBlock_->front.load();
  268. if (blockFront != blockTail ||
  269. blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
  270. fence(memory_order_acquire);
  271. non_empty_front_block:
  272. // Front block not empty, dequeue from here
  273. auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
  274. result = std::move(*element);
  275. element->~T();
  276. blockFront = (blockFront + 1) & frontBlock_->sizeMask;
  277. fence(memory_order_release);
  278. frontBlock_->front = blockFront;
  279. } else if (frontBlock_ != tailBlock.load()) {
  280. fence(memory_order_acquire);
  281. frontBlock_ = frontBlock.load();
  282. blockTail = frontBlock_->localTail = frontBlock_->tail.load();
  283. blockFront = frontBlock_->front.load();
  284. fence(memory_order_acquire);
  285. if (blockFront != blockTail) {
  286. // Oh look, the front block isn't empty after all
  287. goto non_empty_front_block;
  288. }
  289. // Front block is empty but there's another block ahead, advance to it
  290. Block* nextBlock = frontBlock_->next;
  291. // Don't need an acquire fence here since next can only ever be set on the tailBlock,
  292. // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock
  293. // which ensures next is up-to-date on this CPU in case we recently were at tailBlock.
  294. size_t nextBlockFront = nextBlock->front.load();
  295. size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
  296. fence(memory_order_acquire);
  297. // Since the tailBlock is only ever advanced after being written to,
  298. // we know there's for sure an element to dequeue on it
  299. assert(nextBlockFront != nextBlockTail);
  300. AE_UNUSED(nextBlockTail);
  301. // We're done with this block, let the producer use it if it needs
  302. fence(memory_order_release); // Expose possibly pending changes to frontBlock->front
  303. // from last dequeue
  304. frontBlock = frontBlock_ = nextBlock;
  305. compiler_fence(memory_order_release); // Not strictly needed
  306. auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
  307. result = std::move(*element);
  308. element->~T();
  309. nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
  310. fence(memory_order_release);
  311. frontBlock_->front = nextBlockFront;
  312. } else {
  313. // No elements in current block and no other block to advance to
  314. return false;
  315. }
  316. return true;
  317. }
  318. // Returns a pointer to the front element in the queue (the one that
  319. // would be removed next by a call to `try_dequeue` or `pop`). If the
  320. // queue appears empty at the time the method is called, nullptr is
  321. // returned instead.
  322. // Must be called only from the consumer thread.
  323. T* peek() const AE_NO_TSAN {
  324. #ifndef NDEBUG
  325. ReentrantGuard guard(this->dequeuing);
  326. #endif
  327. // See try_dequeue() for reasoning
  328. Block* frontBlock_ = frontBlock.load();
  329. size_t blockTail = frontBlock_->localTail;
  330. size_t blockFront = frontBlock_->front.load();
  331. if (blockFront != blockTail ||
  332. blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
  333. fence(memory_order_acquire);
  334. non_empty_front_block:
  335. return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
  336. } else if (frontBlock_ != tailBlock.load()) {
  337. fence(memory_order_acquire);
  338. frontBlock_ = frontBlock.load();
  339. blockTail = frontBlock_->localTail = frontBlock_->tail.load();
  340. blockFront = frontBlock_->front.load();
  341. fence(memory_order_acquire);
  342. if (blockFront != blockTail) {
  343. goto non_empty_front_block;
  344. }
  345. Block* nextBlock = frontBlock_->next;
  346. size_t nextBlockFront = nextBlock->front.load();
  347. fence(memory_order_acquire);
  348. assert(nextBlockFront != nextBlock->tail.load());
  349. return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
  350. }
  351. return nullptr;
  352. }
  353. // Removes the front element from the queue, if any, without returning it.
  354. // Returns true on success, or false if the queue appeared empty at the time
  355. // `pop` was called.
  356. bool pop() AE_NO_TSAN {
  357. #ifndef NDEBUG
  358. ReentrantGuard guard(this->dequeuing);
  359. #endif
  360. // See try_dequeue() for reasoning
  361. Block* frontBlock_ = frontBlock.load();
  362. size_t blockTail = frontBlock_->localTail;
  363. size_t blockFront = frontBlock_->front.load();
  364. if (blockFront != blockTail ||
  365. blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
  366. fence(memory_order_acquire);
  367. non_empty_front_block:
  368. auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
  369. element->~T();
  370. blockFront = (blockFront + 1) & frontBlock_->sizeMask;
  371. fence(memory_order_release);
  372. frontBlock_->front = blockFront;
  373. } else if (frontBlock_ != tailBlock.load()) {
  374. fence(memory_order_acquire);
  375. frontBlock_ = frontBlock.load();
  376. blockTail = frontBlock_->localTail = frontBlock_->tail.load();
  377. blockFront = frontBlock_->front.load();
  378. fence(memory_order_acquire);
  379. if (blockFront != blockTail) {
  380. goto non_empty_front_block;
  381. }
  382. // Front block is empty but there's another block ahead, advance to it
  383. Block* nextBlock = frontBlock_->next;
  384. size_t nextBlockFront = nextBlock->front.load();
  385. size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
  386. fence(memory_order_acquire);
  387. assert(nextBlockFront != nextBlockTail);
  388. AE_UNUSED(nextBlockTail);
  389. fence(memory_order_release);
  390. frontBlock = frontBlock_ = nextBlock;
  391. compiler_fence(memory_order_release);
  392. auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
  393. element->~T();
  394. nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
  395. fence(memory_order_release);
  396. frontBlock_->front = nextBlockFront;
  397. } else {
  398. // No elements in current block and no other block to advance to
  399. return false;
  400. }
  401. return true;
  402. }
  403. // Returns the approximate number of items currently in the queue.
  404. // Safe to call from both the producer and consumer threads.
  405. inline size_t size_approx() const AE_NO_TSAN {
  406. size_t result = 0;
  407. Block* frontBlock_ = frontBlock.load();
  408. Block* block = frontBlock_;
  409. do {
  410. fence(memory_order_acquire);
  411. size_t blockFront = block->front.load();
  412. size_t blockTail = block->tail.load();
  413. result += (blockTail - blockFront) & block->sizeMask;
  414. block = block->next.load();
  415. } while (block != frontBlock_);
  416. return result;
  417. }
  418. // Returns the total number of items that could be enqueued without incurring
  419. // an allocation when this queue is empty.
  420. // Safe to call from both the producer and consumer threads.
  421. //
  422. // NOTE: The actual capacity during usage may be different depending on the consumer.
  423. // If the consumer is removing elements concurrently, the producer cannot add to
  424. // the block the consumer is removing from until it's completely empty, except in
  425. // the case where the producer was writing to the same block the consumer was
  426. // reading from the whole time.
  427. inline size_t max_capacity() const {
  428. size_t result = 0;
  429. Block* frontBlock_ = frontBlock.load();
  430. Block* block = frontBlock_;
  431. do {
  432. fence(memory_order_acquire);
  433. result += block->sizeMask;
  434. block = block->next.load();
  435. } while (block != frontBlock_);
  436. return result;
  437. }
  438. private:
  439. enum AllocationMode { CanAlloc, CannotAlloc };
  440. #if MOODYCAMEL_HAS_EMPLACE
  441. template <AllocationMode canAlloc, typename... Args>
  442. bool inner_enqueue(Args&&... args) AE_NO_TSAN
  443. #else
  444. template <AllocationMode canAlloc, typename U>
  445. bool inner_enqueue(U&& element) AE_NO_TSAN
  446. #endif
  447. {
  448. #ifndef NDEBUG
  449. ReentrantGuard guard(this->enqueuing);
  450. #endif
  451. // High-level pseudocode (assuming we're allowed to alloc a new block):
  452. // If room in tail block, add to tail
  453. // Else check next block
  454. // If next block is not the head block, enqueue on next block
  455. // Else create a new block and enqueue there
  456. // Advance tail to the block we just enqueued to
  457. Block* tailBlock_ = tailBlock.load();
  458. size_t blockFront = tailBlock_->localFront;
  459. size_t blockTail = tailBlock_->tail.load();
  460. size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
  461. if (nextBlockTail != blockFront ||
  462. nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
  463. fence(memory_order_acquire);
  464. // This block has room for at least one more element
  465. char* location = tailBlock_->data + blockTail * sizeof(T);
  466. #if MOODYCAMEL_HAS_EMPLACE
  467. new (location) T(std::forward<Args>(args)...);
  468. #else
  469. new (location) T(std::forward<U>(element));
  470. #endif
  471. fence(memory_order_release);
  472. tailBlock_->tail = nextBlockTail;
  473. } else {
  474. fence(memory_order_acquire);
  475. if (tailBlock_->next.load() != frontBlock) {
  476. // Note that the reason we can't advance to the frontBlock and start adding new
  477. // entries there is because if we did, then dequeue would stay in that block,
  478. // eventually reading the new values, instead of advancing to the next full block
  479. // (whose values were enqueued first and so should be consumed first).
  480. fence(memory_order_acquire); // Ensure we get latest writes if we got the latest
  481. // frontBlock
  482. // tailBlock is full, but there's a free block ahead, use it
  483. Block* tailBlockNext = tailBlock_->next.load();
  484. size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
  485. nextBlockTail = tailBlockNext->tail.load();
  486. fence(memory_order_acquire);
  487. // This block must be empty since it's not the head block and we
  488. // go through the blocks in a circle
  489. assert(nextBlockFront == nextBlockTail);
  490. tailBlockNext->localFront = nextBlockFront;
  491. char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
  492. #if MOODYCAMEL_HAS_EMPLACE
  493. new (location) T(std::forward<Args>(args)...);
  494. #else
  495. new (location) T(std::forward<U>(element));
  496. #endif
  497. tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
  498. fence(memory_order_release);
  499. tailBlock = tailBlockNext;
  500. } else if (canAlloc == CanAlloc) {
  501. // tailBlock is full and there's no free block ahead; create a new block
  502. auto newBlockSize =
  503. largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
  504. auto newBlock = make_block(newBlockSize);
  505. if (newBlock == nullptr) {
  506. // Could not allocate a block!
  507. return false;
  508. }
  509. largestBlockSize = newBlockSize;
  510. #if MOODYCAMEL_HAS_EMPLACE
  511. new (newBlock->data) T(std::forward<Args>(args)...);
  512. #else
  513. new (newBlock->data) T(std::forward<U>(element));
  514. #endif
  515. assert(newBlock->front == 0);
  516. newBlock->tail = newBlock->localTail = 1;
  517. newBlock->next = tailBlock_->next.load();
  518. tailBlock_->next = newBlock;
  519. // Might be possible for the dequeue thread to see the new tailBlock->next
  520. // *without* seeing the new tailBlock value, but this is OK since it can't
  521. // advance to the next block until tailBlock is set anyway (because the only
  522. // case where it could try to read the next is if it's already at the tailBlock,
  523. // and it won't advance past tailBlock in any circumstance).
  524. fence(memory_order_release);
  525. tailBlock = newBlock;
  526. } else if (canAlloc == CannotAlloc) {
  527. // Would have had to allocate a new block to enqueue, but not allowed
  528. return false;
  529. } else {
  530. assert(false && "Should be unreachable code");
  531. return false;
  532. }
  533. }
  534. return true;
  535. }
  536. // Disable copying
  537. ReaderWriterQueue(ReaderWriterQueue const&) {}
  538. // Disable assignment
  539. ReaderWriterQueue& operator=(ReaderWriterQueue const&) {}
  540. AE_FORCEINLINE static size_t ceilToPow2(size_t x) {
  541. // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
  542. --x;
  543. x |= x >> 1;
  544. x |= x >> 2;
  545. x |= x >> 4;
  546. for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
  547. x |= x >> (i << 3);
  548. }
  549. ++x;
  550. return x;
  551. }
  552. template <typename U>
  553. static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN {
  554. const std::size_t alignment = std::alignment_of<U>::value;
  555. return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
  556. }
  557. private:
  558. #ifndef NDEBUG
  559. struct ReentrantGuard {
  560. AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection) : inSection(_inSection) {
  561. assert(!inSection &&
  562. "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one "
  563. "thread at a time may hold the producer or consumer role)");
  564. inSection = true;
  565. }
  566. AE_NO_TSAN ~ReentrantGuard() {
  567. inSection = false;
  568. }
  569. private:
  570. ReentrantGuard& operator=(ReentrantGuard const&);
  571. private:
  572. weak_atomic<bool>& inSection;
  573. };
  574. #endif
  575. struct Block {
  576. // Avoid false-sharing by putting highly contended variables on their own cache lines
  577. weak_atomic<size_t> front; // (Atomic) Elements are read from here
  578. size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
  579. char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
  580. sizeof(size_t)];
  581. weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
  582. size_t localFront;
  583. char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
  584. sizeof(size_t)]; // next isn't very contended, but we don't want it on
  585. // the same cache line as tail (which is)
  586. weak_atomic<Block*> next; // (Atomic)
  587. char* data; // Contents (on heap) are aligned to T's alignment
  588. const size_t sizeMask;
  589. // size must be a power of two (and greater than 0)
  590. AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
  591. : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data),
  592. sizeMask(_size - 1), rawThis(_rawThis) {}
  593. private:
  594. // C4512 - Assignment operator could not be generated
  595. Block& operator=(Block const&);
  596. public:
  597. char* rawThis;
  598. };
  599. static Block* make_block(size_t capacity) AE_NO_TSAN {
  600. // Allocate enough memory for the block itself, as well as all the elements it will contain
  601. auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
  602. size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
  603. auto newBlockRaw = static_cast<char*>(std::malloc(size));
  604. if (newBlockRaw == nullptr) {
  605. return nullptr;
  606. }
  607. auto newBlockAligned = align_for<Block>(newBlockRaw);
  608. auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
  609. return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
  610. }
  611. private:
  612. weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
  613. char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
  614. weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
  615. size_t largestBlockSize;
  616. #ifndef NDEBUG
  617. weak_atomic<bool> enqueuing;
  618. mutable weak_atomic<bool> dequeuing;
  619. #endif
  620. };
  621. // Like ReaderWriterQueue, but also providees blocking operations
  622. template <typename T, size_t MAX_BLOCK_SIZE = 512>
  623. class BlockingReaderWriterQueue {
  624. private:
  625. typedef ::Common::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
  626. public:
  627. explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
  628. : inner(size),
  629. sema(new spsc_sema::LightweightSemaphore()) {}
  630. BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
  631. : inner(std::move(other.inner)),
  632. sema(std::move(other.sema)) {}
  633. BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN {
  634. std::swap(sema, other.sema);
  635. std::swap(inner, other.inner);
  636. return *this;
  637. }
  638. // Enqueues a copy of element if there is room in the queue.
  639. // Returns true if the element was enqueued, false otherwise.
  640. // Does not allocate memory.
  641. AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
  642. if (inner.try_enqueue(element)) {
  643. sema->signal();
  644. return true;
  645. }
  646. return false;
  647. }
  648. // Enqueues a moved copy of element if there is room in the queue.
  649. // Returns true if the element was enqueued, false otherwise.
  650. // Does not allocate memory.
  651. AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
  652. if (inner.try_enqueue(std::forward<T>(element))) {
  653. sema->signal();
  654. return true;
  655. }
  656. return false;
  657. }
  658. #if MOODYCAMEL_HAS_EMPLACE
  659. // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
  660. template <typename... Args>
  661. AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
  662. if (inner.try_emplace(std::forward<Args>(args)...)) {
  663. sema->signal();
  664. return true;
  665. }
  666. return false;
  667. }
  668. #endif
  669. // Enqueues a copy of element on the queue.
  670. // Allocates an additional block of memory if needed.
  671. // Only fails (returns false) if memory allocation fails.
  672. AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
  673. if (inner.enqueue(element)) {
  674. sema->signal();
  675. return true;
  676. }
  677. return false;
  678. }
  679. // Enqueues a moved copy of element on the queue.
  680. // Allocates an additional block of memory if needed.
  681. // Only fails (returns false) if memory allocation fails.
  682. AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
  683. if (inner.enqueue(std::forward<T>(element))) {
  684. sema->signal();
  685. return true;
  686. }
  687. return false;
  688. }
  689. #if MOODYCAMEL_HAS_EMPLACE
  690. // Like enqueue() but with emplace semantics (i.e. construct-in-place).
  691. template <typename... Args>
  692. AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
  693. if (inner.emplace(std::forward<Args>(args)...)) {
  694. sema->signal();
  695. return true;
  696. }
  697. return false;
  698. }
  699. #endif
  700. // Attempts to dequeue an element; if the queue is empty,
  701. // returns false instead. If the queue has at least one element,
  702. // moves front to result using operator=, then returns true.
  703. template <typename U>
  704. bool try_dequeue(U& result) AE_NO_TSAN {
  705. if (sema->tryWait()) {
  706. bool success = inner.try_dequeue(result);
  707. assert(success);
  708. AE_UNUSED(success);
  709. return true;
  710. }
  711. return false;
  712. }
  713. // Attempts to dequeue an element; if the queue is empty,
  714. // waits until an element is available, then dequeues it.
  715. template <typename U>
  716. void wait_dequeue(U& result) AE_NO_TSAN {
  717. while (!sema->wait())
  718. ;
  719. bool success = inner.try_dequeue(result);
  720. AE_UNUSED(result);
  721. assert(success);
  722. AE_UNUSED(success);
  723. }
  724. // Attempts to dequeue an element; if the queue is empty,
  725. // waits until an element is available up to the specified timeout,
  726. // then dequeues it and returns true, or returns false if the timeout
  727. // expires before an element can be dequeued.
  728. // Using a negative timeout indicates an indefinite timeout,
  729. // and is thus functionally equivalent to calling wait_dequeue.
  730. template <typename U>
  731. bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN {
  732. if (!sema->wait(timeout_usecs)) {
  733. return false;
  734. }
  735. bool success = inner.try_dequeue(result);
  736. AE_UNUSED(result);
  737. assert(success);
  738. AE_UNUSED(success);
  739. return true;
  740. }
  741. #if __cplusplus > 199711L || _MSC_VER >= 1700
  742. // Attempts to dequeue an element; if the queue is empty,
  743. // waits until an element is available up to the specified timeout,
  744. // then dequeues it and returns true, or returns false if the timeout
  745. // expires before an element can be dequeued.
  746. // Using a negative timeout indicates an indefinite timeout,
  747. // and is thus functionally equivalent to calling wait_dequeue.
  748. template <typename U, typename Rep, typename Period>
  749. inline bool wait_dequeue_timed(U& result,
  750. std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN {
  751. return wait_dequeue_timed(
  752. result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
  753. }
  754. #endif
  755. // Returns a pointer to the front element in the queue (the one that
  756. // would be removed next by a call to `try_dequeue` or `pop`). If the
  757. // queue appears empty at the time the method is called, nullptr is
  758. // returned instead.
  759. // Must be called only from the consumer thread.
  760. AE_FORCEINLINE T* peek() const AE_NO_TSAN {
  761. return inner.peek();
  762. }
  763. // Removes the front element from the queue, if any, without returning it.
  764. // Returns true on success, or false if the queue appeared empty at the time
  765. // `pop` was called.
  766. AE_FORCEINLINE bool pop() AE_NO_TSAN {
  767. if (sema->tryWait()) {
  768. bool result = inner.pop();
  769. assert(result);
  770. AE_UNUSED(result);
  771. return true;
  772. }
  773. return false;
  774. }
  775. // Returns the approximate number of items currently in the queue.
  776. // Safe to call from both the producer and consumer threads.
  777. AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN {
  778. return sema->availableApprox();
  779. }
  780. // Returns the total number of items that could be enqueued without incurring
  781. // an allocation when this queue is empty.
  782. // Safe to call from both the producer and consumer threads.
  783. //
  784. // NOTE: The actual capacity during usage may be different depending on the consumer.
  785. // If the consumer is removing elements concurrently, the producer cannot add to
  786. // the block the consumer is removing from until it's completely empty, except in
  787. // the case where the producer was writing to the same block the consumer was
  788. // reading from the whole time.
  789. AE_FORCEINLINE size_t max_capacity() const {
  790. return inner.max_capacity();
  791. }
  792. private:
  793. // Disable copying & assignment
  794. BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) {}
  795. BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) {}
  796. private:
  797. ReaderWriterQueue inner;
  798. std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
  799. };
  800. } // namespace Common
  801. #ifdef AE_VCPP
  802. #pragma warning(pop)
  803. #endif