MCPcopy Create free account
hub / github.com/dmlc/dmlc-core / dequeue

Method dequeue

include/dmlc/concurrentqueue.h:1902–2000  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

1900
1901 template<typename U>
1902 bool dequeue(U &element) {
1903 auto tail = this->tailIndex.load(std::memory_order_relaxed);
1904 auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
1905 if (details::circular_less_than<index_t>(
1906 this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
1907 // Might be something to dequeue, let's give it a try
1908
1909 // Note that this if is purely for performance purposes in the common case when the queue is
1910 // empty and the values are eventually consistent -- we may enter here spuriously.
1911
1912 // Note that whatever the values of overcommit and tail are, they are not going to change (unless we
1913 // change them) and must be the same value at this point (inside the if) as when the if condition was
1914 // evaluated.
1915
1916 // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below.
1917 // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in
1918 // the fetch_add below will result in a value at least as recent as that (and therefore at least as large).
1919 // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all
1920 // read-modify-write operations are guaranteed to work on the latest value in the modification order), but
1921 // unfortunately that can't be shown to be correct using only the C++11 standard.
1922 // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case
1923 std::atomic_thread_fence(std::memory_order_acquire);
1924
1925 // Increment optimistic counter, then check if it went over the boundary
1926 auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
1927
1928 // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever
1929 // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
1930 // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
1931 // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
1932 assert(overcommit <= myDequeueCount);
1933
1934 // Note that we reload tail here in case it changed; it will be the same value as before or greater, since
1935 // this load is sequenced after (happens after) the earlier load above. This is supported by read-read
1936 // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
1937 tail = this->tailIndex.load(std::memory_order_acquire);
1938 if (details::likely(
1939 details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
1940 // Guaranteed to be at least one element to dequeue!
1941
1942 // Get the index. Note that since there's guaranteed to be at least one element, this
1943 // will never exceed tail. We need to do an acquire-release fence here since it's possible
1944 // that whatever condition got us to this point was for an earlier enqueued element (that
1945 // we already see the memory effects for), but that by the time we increment somebody else
1946 // has incremented it, and we need to see the memory effects for *that* element, which is
1947 // in such a case is necessarily visible on the thread that incremented it in the first
1948 // place with the more current condition (they must have acquired a tail that is at least
1949 // as recent).
1950 auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel);
1951
1952
1953 // Determine which block the element is in
1954
1955 auto localBlockIndex = blockIndex.load(std::memory_order_acquire);
1956 auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire);
1957
1958 // We need to be careful here about subtracting and dividing because of index wrap-around.
1959 // When an index wraps, we need to preserve the sign of the offset when dividing it by the

Callers 4

try_dequeueMethod · 0.45
dequeueMethod · 0.45

Calls 1

likelyFunction · 0.85

Tested by

no test coverage detected