proxygen
UnboundedQueue.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <atomic>
20 #include <chrono>
21 #include <memory>
22 
23 #include <glog/logging.h>
24 
25 #include <folly/ConstexprMath.h>
26 #include <folly/Optional.h>
28 #include <folly/lang/Align.h>
33 
34 namespace folly {
35 
207 
208 template <
209  typename T,
210  bool SingleProducer,
211  bool SingleConsumer,
212  bool MayBlock,
213  size_t LgSegmentSize = 8,
215  template <typename> class Atom = std::atomic>
217  using Ticket = uint64_t;
218  class Entry;
219  class Segment;
220 
221  static constexpr bool SPSC = SingleProducer && SingleConsumer;
222  static constexpr size_t Stride = SPSC || (LgSegmentSize <= 1) ? 1 : 27;
223  static constexpr size_t SegmentSize = 1u << LgSegmentSize;
224  static constexpr size_t Align = 1u << LgAlign;
225 
226  static_assert(
228  "T must be nothrow_destructible");
229  static_assert((Stride & 1) == 1, "Stride must be odd");
230  static_assert(LgSegmentSize < 32, "LgSegmentSize must be < 32");
231  static_assert(LgAlign < 16, "LgAlign must be < 16");
232 
233  struct Consumer {
234  Atom<Segment*> head;
235  Atom<Ticket> ticket;
236  explicit Consumer(Segment* s) : head(s), ticket(0) {}
237  };
238  struct Producer {
239  Atom<Segment*> tail;
240  Atom<Ticket> ticket;
241  explicit Producer(Segment* s) : tail(s), ticket(0) {}
242  };
243 
244  alignas(Align) Consumer c_;
245  alignas(Align) Producer p_;
246 
247  public:
250  : c_(new Segment(0)), p_(c_.head.load(std::memory_order_relaxed)) {}
251 
256  }
257 
259  FOLLY_ALWAYS_INLINE void enqueue(const T& arg) {
260  enqueueImpl(arg);
261  }
262 
264  enqueueImpl(std::move(arg));
265  }
266 
269  dequeueImpl(item);
270  }
271 
274  auto o = try_dequeue();
275  if (LIKELY(o.has_value())) {
276  item = std::move(*o);
277  return true;
278  }
279  return false;
280  }
281 
284  }
285 
287  template <typename Clock, typename Duration>
289  T& item,
290  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
292 
293  if (LIKELY(o.has_value())) {
294  item = std::move(*o);
295  return true;
296  }
297 
298  return false;
299  }
300 
301  template <typename Clock, typename Duration>
303  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
304  return tryDequeueUntil(deadline);
305  }
306 
308  template <typename Rep, typename Period>
310  T& item,
311  const std::chrono::duration<Rep, Period>& duration) noexcept {
312  folly::Optional<T> o = try_dequeue_for(duration);
313 
314  if (LIKELY(o.has_value())) {
315  item = std::move(*o);
316  return true;
317  }
318 
319  return false;
320  }
321 
322  template <typename Rep, typename Period>
324  const std::chrono::duration<Rep, Period>& duration) noexcept {
326  if (LIKELY(o.has_value())) {
327  return o;
328  }
329  return tryDequeueUntil(std::chrono::steady_clock::now() + duration);
330  }
331 
334  /* This function is supported only for USPSC and UMPSC queues. */
335  DCHECK(SingleConsumer);
337  }
338 
340  size_t size() const noexcept {
341  auto p = producerTicket();
342  auto c = consumerTicket();
343  return p > c ? p - c : 0;
344  }
345 
348  auto c = consumerTicket();
349  auto p = producerTicket();
350  return p <= c;
351  }
352 
353  private:
355  template <typename Arg>
357  if (SPSC) {
358  Segment* s = tail();
359  enqueueCommon(s, std::forward<Arg>(arg));
360  } else {
361  // Using hazptr_holder instead of hazptr_local because it is
362  // possible that the T ctor happens to use hazard pointers.
363  hazptr_holder<Atom> hptr;
364  Segment* s = hptr.get_protected(p_.tail);
365  enqueueCommon(s, std::forward<Arg>(arg));
366  }
367  }
368 
370  template <typename Arg>
371  FOLLY_ALWAYS_INLINE void enqueueCommon(Segment* s, Arg&& arg) {
373  if (!SingleProducer) {
374  s = findSegment(s, t);
375  }
376  DCHECK_GE(t, s->minTicket());
377  DCHECK_LT(t, s->minTicket() + SegmentSize);
378  size_t idx = index(t);
379  Entry& e = s->entry(idx);
380  e.putItem(std::forward<Arg>(arg));
381  if (responsibleForAlloc(t)) {
382  allocNextSegment(s);
383  }
384  if (responsibleForAdvance(t)) {
385  advanceTail(s);
386  }
387  }
388 
391  if (SPSC) {
392  Segment* s = head();
393  dequeueCommon(s, item);
394  } else {
395  // Using hazptr_holder instead of hazptr_local because it is
396  // possible to call the T dtor and it may happen to use hazard
397  // pointers.
398  hazptr_holder<Atom> hptr;
399  Segment* s = hptr.get_protected(c_.head);
400  dequeueCommon(s, item);
401  }
402  }
403 
405  FOLLY_ALWAYS_INLINE void dequeueCommon(Segment* s, T& item) noexcept {
407  if (!SingleConsumer) {
408  s = findSegment(s, t);
409  }
410  size_t idx = index(t);
411  Entry& e = s->entry(idx);
412  e.takeItem(item);
413  if (responsibleForAdvance(t)) {
414  advanceHead(s);
415  }
416  }
417 
419  template <typename Clock, typename Duration>
421  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
422  if (SingleConsumer) {
423  Segment* s = head();
424  return tryDequeueUntilSC(s, deadline);
425  } else {
426  // Using hazptr_holder instead of hazptr_local because it is
427  // possible to call ~T() and it may happen to use hazard pointers.
428  hazptr_holder<Atom> hptr;
429  Segment* s = hptr.get_protected(c_.head);
430  return tryDequeueUntilMC(s, deadline);
431  }
432  }
433 
435  template <typename Clock, typename Duration>
437  Segment* s,
438  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
439  Ticket t = consumerTicket();
440  DCHECK_GE(t, s->minTicket());
441  DCHECK_LT(t, (s->minTicket() + SegmentSize));
442  size_t idx = index(t);
443  Entry& e = s->entry(idx);
444  if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {
445  return folly::Optional<T>();
446  }
447  setConsumerTicket(t + 1);
448  auto ret = e.takeItem();
449  if (responsibleForAdvance(t)) {
450  advanceHead(s);
451  }
452  return ret;
453  }
454 
456  template <typename Clock, typename Duration>
458  Segment* s,
459  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
460  while (true) {
461  Ticket t = consumerTicket();
462  if (UNLIKELY(t >= (s->minTicket() + SegmentSize))) {
463  s = getAllocNextSegment(s, t);
464  DCHECK(s);
465  continue;
466  }
467  size_t idx = index(t);
468  Entry& e = s->entry(idx);
469  if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {
470  return folly::Optional<T>();
471  }
472  if (!c_.ticket.compare_exchange_weak(
473  t, t + 1, std::memory_order_acq_rel, std::memory_order_acquire)) {
474  continue;
475  }
476  auto ret = e.takeItem();
477  if (responsibleForAdvance(t)) {
478  advanceHead(s);
479  }
480  return ret;
481  }
482  }
483 
485  template <typename Clock, typename Duration>
487  Entry& e,
488  Ticket t,
489  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
490  if (LIKELY(e.tryWaitUntil(deadline))) {
491  return true;
492  }
493  return t < producerTicket();
494  }
495 
497  template <typename Clock, typename Duration>
499  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
500  Segment* s = head();
501  Ticket t = consumerTicket();
502  DCHECK_GE(t, s->minTicket());
503  DCHECK_LT(t, (s->minTicket() + SegmentSize));
504  size_t idx = index(t);
505  Entry& e = s->entry(idx);
506  if (UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {
507  return nullptr;
508  }
509  return e.peekItem();
510  }
511 
514  Segment* findSegment(Segment* s, const Ticket t) noexcept {
515  while (UNLIKELY(t >= (s->minTicket() + SegmentSize))) {
516  s = getAllocNextSegment(s, t);
517  DCHECK(s);
518  }
519  return s;
520  }
521 
523  Segment* getAllocNextSegment(Segment* s, Ticket t) noexcept {
524  Segment* next = s->nextSegment();
525  if (!next) {
526  DCHECK_GE(t, s->minTicket() + SegmentSize);
527  auto diff = t - (s->minTicket() + SegmentSize);
528  if (diff > 0) {
529  auto dur = std::chrono::microseconds(diff);
530  auto deadline = std::chrono::steady_clock::now() + dur;
531  WaitOptions opt;
532  opt.spin_max(dur);
534  deadline, opt, [s] { return s->nextSegment(); });
535  next = s->nextSegment();
536  if (next) {
537  return next;
538  }
539  }
540  next = allocNextSegment(s);
541  }
542  DCHECK(next);
543  return next;
544  }
545 
547  Segment* allocNextSegment(Segment* s) {
548  auto t = s->minTicket() + SegmentSize;
549  Segment* next = new Segment(t);
550  next->acquire_ref_safe(); // defined in hazptr_obj_base_linked
551  if (!s->casNextSegment(next)) {
552  delete next;
553  next = s->nextSegment();
554  }
555  DCHECK(next);
556  return next;
557  }
558 
560  void advanceTail(Segment* s) noexcept {
561  if (SPSC) {
562  Segment* next = s->nextSegment();
563  DCHECK(next);
564  setTail(next);
565  } else {
566  Ticket t = s->minTicket() + SegmentSize;
568  }
569  }
570 
573  Segment* s = tail();
574  while (s->minTicket() < t) {
575  Segment* next = s->nextSegment();
576  if (!next) {
577  next = allocNextSegment(s);
578  }
579  DCHECK(next);
580  casTail(s, next);
581  s = tail();
582  }
583  }
584 
586  void advanceHead(Segment* s) noexcept {
587  if (SPSC) {
588  while (tail() == s) {
589  /* Wait for producer to advance tail. */
591  }
592  Segment* next = s->nextSegment();
593  DCHECK(next);
594  setHead(next);
595  reclaimSegment(s);
596  } else {
597  Ticket t = s->minTicket() + SegmentSize;
599  }
600  }
601 
604  /* Tail must not lag behind head. Otherwise, the algorithm cannot
605  be certain about removal of segments. */
607  Segment* s = head();
608  if (SingleConsumer) {
609  DCHECK_EQ(s->minTicket() + SegmentSize, t);
610  Segment* next = s->nextSegment();
611  DCHECK(next);
612  setHead(next);
613  reclaimSegment(s);
614  } else {
615  while (s->minTicket() < t) {
616  Segment* next = s->nextSegment();
617  DCHECK(next);
618  if (casHead(s, next)) {
619  reclaimSegment(s);
620  s = next;
621  }
622  }
623  }
624  }
625 
627  void reclaimSegment(Segment* s) noexcept {
628  if (SPSC) {
629  delete s;
630  } else {
631  s->retire(); // defined in hazptr_obj_base_linked
632  }
633  }
634 
637  auto end = producerTicket();
638  auto s = head();
639  for (auto t = consumerTicket(); t < end; ++t) {
640  if (t >= s->minTicket() + SegmentSize) {
641  s = s->nextSegment();
642  }
643  DCHECK_LT(t, (s->minTicket() + SegmentSize));
644  auto idx = index(t);
645  auto& e = s->entry(idx);
646  e.destroyItem();
647  }
648  }
649 
652  auto h = head();
653  auto s = h->nextSegment();
654  h->setNextSegment(nullptr);
655  reclaimSegment(h);
656  while (s) {
657  auto next = s->nextSegment();
658  delete s;
659  s = next;
660  }
661  }
662 
664  return (t * Stride) & (SegmentSize - 1);
665  }
666 
668  return (t & (SegmentSize - 1)) == 0;
669  }
670 
672  return (t & (SegmentSize - 1)) == (SegmentSize - 1);
673  }
674 
676  return c_.head.load(std::memory_order_acquire);
677  }
678 
680  return p_.tail.load(std::memory_order_acquire);
681  }
682 
684  return p_.ticket.load(std::memory_order_acquire);
685  }
686 
688  return c_.ticket.load(std::memory_order_acquire);
689  }
690 
691  void setHead(Segment* s) noexcept {
692  DCHECK(SingleConsumer);
693  c_.head.store(s, std::memory_order_relaxed);
694  }
695 
696  void setTail(Segment* s) noexcept {
697  DCHECK(SPSC);
698  p_.tail.store(s, std::memory_order_release);
699  }
700 
701  bool casHead(Segment*& s, Segment* next) noexcept {
702  DCHECK(!SingleConsumer);
703  return c_.head.compare_exchange_strong(
704  s, next, std::memory_order_release, std::memory_order_acquire);
705  }
706 
707  void casTail(Segment*& s, Segment* next) noexcept {
708  DCHECK(!SPSC);
709  p_.tail.compare_exchange_strong(
710  s, next, std::memory_order_release, std::memory_order_relaxed);
711  }
712 
714  p_.ticket.store(t, std::memory_order_release);
715  }
716 
718  c_.ticket.store(t, std::memory_order_release);
719  }
720 
722  if (SingleConsumer) {
723  Ticket oldval = consumerTicket();
724  setConsumerTicket(oldval + 1);
725  return oldval;
726  } else { // MC
727  return c_.ticket.fetch_add(1, std::memory_order_acq_rel);
728  }
729  }
730 
732  if (SingleProducer) {
733  Ticket oldval = producerTicket();
734  setProducerTicket(oldval + 1);
735  return oldval;
736  } else { // MP
737  return p_.ticket.fetch_add(1, std::memory_order_acq_rel);
738  }
739  }
740 
744  class Entry {
747 
748  public:
749  template <typename Arg>
750  FOLLY_ALWAYS_INLINE void putItem(Arg&& arg) {
751  new (&item_) T(std::forward<Arg>(arg));
752  flag_.post();
753  }
754 
756  flag_.wait();
757  getItem(item);
758  }
759 
761  flag_.wait();
762  return getItem();
763  }
764 
766  flag_.wait();
767  return itemPtr();
768  }
769 
770  template <typename Clock, typename Duration>
772  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
773  // wait-options from benchmarks on contended queues:
774  auto const opt =
775  flag_.wait_options().spin_max(std::chrono::microseconds(10));
776  return flag_.try_wait_until(deadline, opt);
777  }
778 
780  itemPtr()->~T();
781  }
782 
783  private:
785  item = std::move(*(itemPtr()));
786  destroyItem();
787  }
788 
790  folly::Optional<T> ret = std::move(*(itemPtr()));
791  destroyItem();
792  return ret;
793  }
794 
796  return static_cast<T*>(static_cast<void*>(&item_));
797  }
798  }; // Entry
799 
803  class Segment : public hazptr_obj_base_linked<Segment, Atom> {
804  Atom<Segment*> next_{nullptr};
805  const Ticket min_;
806  alignas(Align) Entry b_[SegmentSize];
807 
808  public:
809  explicit Segment(const Ticket t) noexcept : min_(t) {}
810 
812  return next_.load(std::memory_order_acquire);
813  }
814 
816  next_.store(next, std::memory_order_relaxed);
817  }
818 
820  Segment* expected = nullptr;
821  return next_.compare_exchange_strong(
822  expected, next, std::memory_order_release, std::memory_order_relaxed);
823  }
824 
826  DCHECK_EQ((min_ & (SegmentSize - 1)), Ticket(0));
827  return min_;
828  }
829 
831  return b_[index];
832  }
833 
834  template <typename S>
835  void push_links(bool m, S& s) {
836  if (m == false) { // next_ is immutable
837  auto p = nextSegment();
838  if (p) {
839  s.push(p);
840  }
841  }
842  }
843  }; // Segment
844 
845 }; // UnboundedQueue
846 
847 /* Aliases */
848 
849 template <
850  typename T,
851  bool MayBlock,
852  size_t LgSegmentSize = 8,
854  template <typename> class Atom = std::atomic>
855 using USPSCQueue =
857 
858 template <
859  typename T,
860  bool MayBlock,
861  size_t LgSegmentSize = 8,
863  template <typename> class Atom = std::atomic>
864 using UMPSCQueue =
866 
867 template <
868  typename T,
869  bool MayBlock,
870  size_t LgSegmentSize = 8,
872  template <typename> class Atom = std::atomic>
873 using USPMCQueue =
875 
876 template <
877  typename T,
878  bool MayBlock,
879  size_t LgSegmentSize = 8,
881  template <typename> class Atom = std::atomic>
882 using UMPMCQueue =
884 
885 } // namespace folly
FOLLY_ALWAYS_INLINE Segment * findSegment(Segment *s, const Ticket t) noexcept
FOLLY_ALWAYS_INLINE void wait(const WaitOptions &opt=wait_options()) noexcept
FOLLY_ALWAYS_INLINE const T * try_peek() noexcept
*than *hazptr_holder h
Definition: Hazptr.h:116
FOLLY_ALWAYS_INLINE bool responsibleForAdvance(Ticket t) const noexcept
Segment * getAllocNextSegment(Segment *s, Ticket t) noexcept
std::chrono::nanoseconds spin_max() const
Definition: WaitOptions.h:58
FOLLY_ALWAYS_INLINE folly::Optional< T > takeItem() noexcept
FOLLY_ALWAYS_INLINE void destroyItem() noexcept
Segment * allocNextSegment(Segment *s)
FOLLY_ALWAYS_INLINE void setProducerTicket(Ticket t) noexcept
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
FOLLY_ALWAYS_INLINE T * get_protected(const Atom< T * > &src) noexcept
Definition: HazptrHolder.h:138
static constexpr size_t Align
FOLLY_ALWAYS_INLINE folly::Optional< T > tryDequeueUntil(const std::chrono::time_point< Clock, Duration > &deadline) noexcept
PskType type
bool casNextSegment(Segment *next) noexcept
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
#define LIKELY(x)
Definition: Likely.h:47
STL namespace.
FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept
Definition: Optional.h:296
void advanceTail(Segment *s) noexcept
FOLLY_ALWAYS_INLINE void putItem(Arg &&arg)
size_t size() const noexcept
constexpr T constexpr_log2(T t)
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
folly::SaturatingSemaphore< MayBlock, Atom > flag_
FOLLY_ALWAYS_INLINE void getItem(T &item) noexcept
requires E e noexcept(noexcept(s.error(std::move(e))))
const int min_
def load()
Definition: deadlock.py:441
FOLLY_ALWAYS_INLINE Ticket fetchIncrementConsumerTicket() noexcept
FOLLY_ALWAYS_INLINE folly::Optional< T > getItem() noexcept
void setTail(Segment *s) noexcept
FOLLY_ALWAYS_INLINE folly::Optional< T > tryDequeueUntilMC(Segment *s, const std::chrono::time_point< Clock, Duration > &deadline) noexcept
static FOLLY_ALWAYS_INLINE WaitOptions wait_options()
FOLLY_ALWAYS_INLINE bool try_dequeue_for(T &item, const std::chrono::duration< Rep, Period > &duration) noexcept
FOLLY_ALWAYS_INLINE void enqueue(T &&arg)
FOLLY_ALWAYS_INLINE void post() noexcept
constexpr std::size_t hardware_destructive_interference_size
Definition: Align.h:107
void setHead(Segment *s) noexcept
FOLLY_ALWAYS_INLINE bool responsibleForAlloc(Ticket t) const noexcept
FOLLY_ALWAYS_INLINE folly::Optional< T > try_dequeue_until(const std::chrono::time_point< Clock, Duration > &deadline) noexcept
FOLLY_ALWAYS_INLINE bool try_dequeue_until(T &item, const std::chrono::time_point< Clock, Duration > &deadline) noexcept
bool casHead(Segment *&s, Segment *next) noexcept
LogLevel min
Definition: LogLevel.cpp:30
FOLLY_ALWAYS_INLINE bool tryDequeueWaitElem(Entry &e, Ticket t, const std::chrono::time_point< Clock, Duration > &deadline) noexcept
std::aligned_storage< sizeof(T), alignof(T)>::type item_
FOLLY_ALWAYS_INLINE const T * peekItem() noexcept
FOLLY_ALWAYS_INLINE Ticket consumerTicket() const noexcept
FOLLY_ALWAYS_INLINE bool try_wait_until(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt=wait_options()) noexcept
#define Atom
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
void advanceHeadToTicket(Ticket t) noexcept
static map< string, int > m
static constexpr size_t SegmentSize
FOLLY_ALWAYS_INLINE void dequeueCommon(Segment *s, T &item) noexcept
FOLLY_ALWAYS_INLINE void setConsumerTicket(Ticket t) noexcept
static const char *const value
Definition: Conv.cpp:50
void reclaimSegment(Segment *s) noexcept
FOLLY_ALWAYS_INLINE size_t index(Ticket t) const noexcept
void advanceTailToTicket(Ticket t) noexcept
Segment * nextSegment() const noexcept
uint64_t diff(uint64_t a, uint64_t b)
Definition: FutexTest.cpp:135
FOLLY_ALWAYS_INLINE T * itemPtr() noexcept
FOLLY_ALWAYS_INLINE Entry & entry(size_t index) noexcept
FOLLY_ALWAYS_INLINE Segment * tail() const noexcept
FOLLY_ALWAYS_INLINE Segment * head() const noexcept
static set< string > s
const
Definition: upload.py:398
static constexpr bool SPSC
FOLLY_ALWAYS_INLINE Ticket producerTicket() const noexcept
void advanceHead(Segment *s) noexcept
FOLLY_ALWAYS_INLINE const T * tryPeekUntil(const std::chrono::time_point< Clock, Duration > &deadline) noexcept
FOLLY_ALWAYS_INLINE void dequeueImpl(T &item) noexcept
Segment(const Ticket t) noexcept
FOLLY_ALWAYS_INLINE Ticket minTicket() const noexcept
FOLLY_ALWAYS_INLINE void enqueueCommon(Segment *s, Arg &&arg)
FOLLY_ALWAYS_INLINE folly::Optional< T > tryDequeueUntilSC(Segment *s, const std::chrono::time_point< Clock, Duration > &deadline) noexcept
FOLLY_ALWAYS_INLINE void takeItem(T &item) noexcept
FOLLY_ALWAYS_INLINE void dequeue(T &item) noexcept
static constexpr size_t Stride
FOLLY_ALWAYS_INLINE void enqueueImpl(Arg &&arg)
#define UNLIKELY(x)
Definition: Likely.h:48
void setNextSegment(Segment *next)
FOLLY_ALWAYS_INLINE void enqueue(const T &arg)
FOLLY_ALWAYS_INLINE Ticket fetchIncrementProducerTicket() noexcept
char c
bool empty() const noexcept
FOLLY_ALWAYS_INLINE bool tryWaitUntil(const std::chrono::time_point< Clock, Duration > &deadline) noexcept
void casTail(Segment *&s, Segment *next) noexcept
spin_result spin_pause_until(std::chrono::time_point< Clock, Duration > const &deadline, WaitOptions const &opt, F f)
Definition: Spin.h:36
FOLLY_ALWAYS_INLINE folly::Optional< T > try_dequeue_for(const std::chrono::duration< Rep, Period > &duration) noexcept
FOLLY_ALWAYS_INLINE folly::Optional< T > try_dequeue() noexcept
def next(obj)
Definition: ast.py:58
void asm_volatile_pause()
Definition: Asm.h:37
FOLLY_ALWAYS_INLINE bool try_dequeue(T &item) noexcept