MCPcopy Create free account
hub / github.com/cameron314/concurrentqueue / try_dequeue

Function try_dequeue

tests/CDSChecker/corealgo.h:50–107  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

48}
49
50static bool try_dequeue(int& element)
51{
52 auto tail = tailIndex.load(std::memory_order_relaxed);
53 auto overcommit = dequeueOvercommit.load(std::memory_order_relaxed);
54 if (circular_less_than<index_t>(dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
55 // Might be something to dequeue, let's give it a try
56
57 // Note that this if is purely for performance purposes in the common case when the queue is
58 // empty and the values are eventually consistent -- we may enter here spuriously.
59
60 // Note that whatever the values of overcommit and tail are, they are not going to change (unless we
61 // change them) and must be the same value at this point (inside the if) as when the if condition was
62 // evaluated.
63
64 // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below.
65 // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in
66 // the fetch_add below will result in a value at least as recent as that (and therefore at least as large).
67 // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all
68 // read-modify-write operations are guaranteed to work on the latest value in the modification order), but
69 // unfortunately that can't be shown to be correct using only the C++11 standard.
70 // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case
71 std::atomic_thread_fence(std::memory_order_acquire);
72
73 // Increment optimistic counter, then check if it went over the boundary
74 auto myDequeueCount = dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
75
76 // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever
77 // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
78 // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
79 // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
80 MODEL_ASSERT(overcommit <= myDequeueCount);
81
82 // Note that we reload tail here in case it changed; it will be the same value as before or greater, since
83 // this load is sequenced after (happens after) the earlier load above. This is supported by read-read
84 // coherance (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
85 auto newTail = tailIndex.load(std::memory_order_acquire);
86 MODEL_ASSERT(newTail >= tail);
87 tail = newTail;
88 if (circular_less_than<index_t>(myDequeueCount - overcommit, tail)) {
89 // Guaranteed to be at least one element to dequeue!
90
91 // Get the index. Note that since there's guaranteed to be at least one element, this
92 // will never exceed the true value of tail (but may exceed the value we read above).
93 auto index = headIndex.fetch_add(1, std::memory_order_acq_rel);
94
95 // Dequeue
96 element = load_32(&block[index & (BLOCK_SIZE - 1)]);
97
98 return true;
99 }
100 else {
101 // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent
102 dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write
103 }
104 }
105
106 return false;
107}

Callers 2

consumerFunction · 0.85
consumer_threadFunction · 0.85

Calls 3

atomic_thread_fenceFunction · 0.85
loadMethod · 0.45
fetch_addMethod · 0.45

Tested by

no test coverage detected