SpscQueue数据结构

 一个SpscQueue的实现。

在Flush函数中将c赋值为f_item的值, 在CheckRead中prefetch时将r_item赋值为c的值。

 

///  Lock-free queue implementation.
///  Only a single thread can read from the queue at any specific moment.
///  Only a single thread can write to the queue at any specific moment.
///  T is the type of the object in the queue.
///  N is granularity of the pipe
template <typename T, int N>
class SpscQueue {
 public:

  // Create the queue.
  inline SpscQueue()
    : r_item_(NULL), w_item_(NULL), f_item_(NULL), contention_(NULL) {}

  //  Destroy the queue.
  inline virtual ~SpscQueue() {}

  /// Initialize the queue. May fail when memory allocation failed.
  inline int Init() {
    CHECK_RESULT(queue_.Init());
    //end_pos point to the next element of back_pos.
    CHECK_RESULT(queue_.Push());

    //  Let all the pointers to point to the beginning of the queue.
    r_item_ = w_item_ = f_item_ = &queue_.Back ();
    contention_.store(&queue_.Back ());
    return 0;
  }

  //  Write an item to the queue.  Don't flush it yet. If incomplete is
  //  set to true the item is assumed to be continued by items
  //  subsequently written to the pipe. Incomplete items are never
  //  flushed down the stream.
  // The items between f_item and back() are uncompleted items.
  // May fail on memory allocation
  inline int Write(const T& value, bool incomplete) {
    //  Place the value to the queue.
    queue_.Back () = value;
    CHECK_RESULT(queue_.Push());

    //  Move the "flush up to here" pointer.
    if (!incomplete) {
      f_item_ = &queue_.Back ();
    }
    return 0;
  }

  //  Remove an incomplete item from the end of queue. Returns true is such
  //  item exists, false otherwise.
  inline bool Unwrite(T* value) {
    if (f_item_ == &queue_.Back ()) {
      return false;
    }
    queue_.Unpush ();
    *value = queue_.Back ();
    return true;
  }
//  Flush makes all the completed items ready to be fetched by Reader. Returns false if
  //  the reader thread is sleeping. In that case, caller is obliged to
  //  wake the reader up before using the pipe again.
  inline bool Flush() {
    //  If there are no un-flushed items, do nothing.
    // The items between w_item and f_item are unflushed items.
    if (w_item_ == f_item_) {
      return true;
    }

    //  Try to set 'c' to 'f'.
    if (!contention_.compare_exchange_strong(w_item_, f_item_,
      boost::memory_order_acq_rel)) {
      //  Compare-and-swap was unsuccessful because 'c' is NULL.
      //  This means that the reader is asleep. Therefore we don't
      //  care about thread-safeness and update c in non-atomic
      //  manner. We'll return false to let the caller know
      //  that reader is sleeping.
      contention_.store(f_item_, boost::memory_order_relaxed);
      w_item_ = f_item_;
      return false;
    }

    //  Reader is alive. Nothing special to do now. Just move
    //  the 'first un-flushed item' pointer to 'f'.
    w_item_ = f_item_;
    return true;
  }

  //  Check whether item is available for reading.
  inline bool CheckRead() {
    //  If there are some prefetched items already, return true.
    //  The items between Front() annd r_item_ are prefetched items.
    if (&queue_.Front () != r_item_ && r_item_) {
      return true;
    }

    //  If there's no prefetched items, let us prefetch some items.
    // The items between r_item and c are unprefetched items.
    //  Front()==c means there is no item to prefetch, then set c to NULL (using compare-and-swap).
    //  Front()!=c means there are items to prefetch,  then assign r_item=c.
    T* first_item = &queue_.Front();
    contention_.compare_exchange_strong(first_item, NULL,
      boost::memory_order_acq_rel);
    r_item_ = first_item;

    //  If there are no elements prefetched, exit.
    //  During pipe's lifetime r should never be NULL, however,
    //  it can happen during pipe shutdown when items
    //  are being deallocated.
    if (&queue_.Front () == r_item_ || !r_item_) {
      return false;
    }

    //  There was at least one value prefetched.
    return true;
  }
//  Reads an item from the pipe. Returns false if there is no value available.
  inline bool Read(T* value) {
    //  Try to prefetch a value.
    if (!CheckRead ())
      return false;

    //  There was at least one value prefetched.
    //  Return it to the caller.
    *value = queue_.Front ();
    queue_.Pop ();
    return true;
  }

  //  Applies the function fn to the first element in the pipe
  //  and returns the value returned by the fn.
  //  The pipe mustn't be empty or the function crashes.
  inline bool Probe (bool (*fn)(T &)) {
    bool rc = CheckRead ();
    if (!rc) return rc;

    return (*fn) (queue_.Front ());
  }

 protected:

  //  Allocation-efficient queue to store spsc queue items.
  //  Front of the queue points to the first prefetched item, back of
  //  the queue points to last un-flushed item. Front is used only by
  //  reader thread, while back is used only by writer thread.
  BatchQueue<T, N> queue_;

  //  Points to the first un-flushed item. This variable is used
  //  exclusively by writer thread.
  T* w_item_;

  //  Points to the first un-prefetched item. This variable is used
  //  exclusively by reader thread.
  T* r_item_;

  // Points to the next item of the last un-flushed item.
  T* f_item_;

  //  The single point of contention between writer and reader thread.
  //  Points past the last flushed item. If it is NULL,
  //  reader is asleep. This pointer should be always accessed using
  //  atomic operations.
  boost::atomic<T*> contention_;

  //  Disable copying of SpscQueue
  DISALLOW_COPY_AND_ASSIGN(SpscQueue);
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值