Returns a pointer to the front element in the queue (the one that would be removed next by a call to `try_dequeue` or `pop`). If the queue appears empty at the time the method is called, nullptr is returned instead. Must be called only from the consumer thread.
| 382 | // returned instead. |
| 383 | // Must be called only from the consumer thread. |
| 384 | T* peek() const AE_NO_TSAN |
| 385 | { |
| 386 | #ifndef NDEBUG |
| 387 | ReentrantGuard guard(this->dequeuing); |
| 388 | #endif |
| 389 | // See try_dequeue() for reasoning |
| 390 | |
| 391 | Block* frontBlock_ = frontBlock.load(); |
| 392 | size_t blockTail = frontBlock_->localTail; |
| 393 | size_t blockFront = frontBlock_->front.load(); |
| 394 | |
| 395 | if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { |
| 396 | fence(memory_order_acquire); |
| 397 | non_empty_front_block: |
| 398 | return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T)); |
| 399 | } |
| 400 | else if (frontBlock_ != tailBlock.load()) { |
| 401 | fence(memory_order_acquire); |
| 402 | frontBlock_ = frontBlock.load(); |
| 403 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); |
| 404 | blockFront = frontBlock_->front.load(); |
| 405 | fence(memory_order_acquire); |
| 406 | |
| 407 | if (blockFront != blockTail) { |
| 408 | goto non_empty_front_block; |
| 409 | } |
| 410 | |
| 411 | Block* nextBlock = frontBlock_->next; |
| 412 | |
| 413 | size_t nextBlockFront = nextBlock->front.load(); |
| 414 | fence(memory_order_acquire); |
| 415 | |
| 416 | assert(nextBlockFront != nextBlock->tail.load()); |
| 417 | return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T)); |
| 418 | } |
| 419 | |
| 420 | return nullptr; |
| 421 | } |
| 422 | |
| 423 | // Removes the front element from the queue, if any, without returning it. |
| 424 | // Returns true on success, or false if the queue appeared empty at the time |