| 1450 | } |
| 1451 | |
| 1452 | inline N* try_get() |
| 1453 | { |
| 1454 | #ifdef MCDBGQ_NOLOCKFREE_FREELIST |
| 1455 | debug::DebugLock lock(mutex); |
| 1456 | #endif |
| 1457 | auto head = freeListHead.load(std::memory_order_acquire); |
| 1458 | while (head != nullptr) { |
| 1459 | auto prevHead = head; |
| 1460 | auto refs = head->freeListRefs.load(std::memory_order_relaxed); |
| 1461 | if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) { |
| 1462 | head = freeListHead.load(std::memory_order_acquire); |
| 1463 | continue; |
| 1464 | } |
| 1465 | |
| 1466 | // Good, reference count has been incremented (it wasn't at zero), which means we can read the |
| 1467 | // next and not worry about it changing between now and the time we do the CAS |
| 1468 | auto next = head->freeListNext.load(std::memory_order_relaxed); |
| 1469 | if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) { |
| 1470 | // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no |
| 1471 | // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). |
| 1472 | assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); |
| 1473 | |
| 1474 | // Decrease refcount twice, once for our ref, and once for the list's ref |
| 1475 | head->freeListRefs.fetch_sub(2, std::memory_order_release); |
| 1476 | return head; |
| 1477 | } |
| 1478 | |
| 1479 | // OK, the head must have changed on us, but we still need to decrease the refcount we increased. |
| 1480 | // Note that we don't need to release any memory effects, but we do need to ensure that the reference |
| 1481 | // count decrement happens-after the CAS on the head. |
| 1482 | refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel); |
| 1483 | if (refs == SHOULD_BE_ON_FREELIST + 1) { |
| 1484 | add_knowing_refcount_is_zero(prevHead); |
| 1485 | } |
| 1486 | } |
| 1487 | |
| 1488 | return nullptr; |
| 1489 | } |
| 1490 | |
| 1491 | // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) |
| 1492 | N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } |
no outgoing calls
no test coverage detected