一个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);
};