-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* Not any company's property but Public-Domain | ||
* Do with source-code as you will. No requirement to keep this | ||
* header if need to use it/change it/ or do whatever with it | ||
* | ||
* Note that there is No guarantee that this code will work | ||
* and I take no responsibility for this code and any problems you | ||
* might get if using it. | ||
* | ||
* Code & platform dependent issues with it was originally | ||
* published at http://www.kjellkod.cc/threadsafecircularqueue | ||
* 2012-16-19 @author Kjell Hedström, [email protected] | ||
* First approach for this was in 2007 I think, a significant update happend in 2009. | ||
* I also wrote an article about it a few years later: | ||
* https://kjellkod.wordpress.com/2012/11/28/c-debt-paid-in-full-wait-free-lock-free-queue/ | ||
* | ||
* Modified from KjellKod's code at: | ||
* https://github.com/KjellKod/lock-free-wait-free-circularfifo | ||
*/ | ||
|
||
// should be mentioned the thinking of what goes where | ||
// it is a "controversy" whether what is tail and what is head | ||
// http://en.wikipedia.org/wiki/FIFO#Head_ortail__first | ||
|
||
#pragma once | ||
|
||
#include <atomic> | ||
#include <cstddef> | ||
#include <thread> | ||
#include <vector> | ||
|
||
namespace spsc { | ||
template <typename Element> | ||
class circular_fifo { | ||
public: | ||
explicit circular_fifo(const size_t size) : | ||
kSize(size), | ||
kCapacity(kSize + 1), | ||
array_(kCapacity), | ||
tail_(0), | ||
head_(0) { | ||
} | ||
|
||
virtual ~circular_fifo() {} | ||
|
||
bool push(Element& item); | ||
bool pop(Element& item); | ||
bool empty() const; | ||
bool full() const; | ||
size_t capacity() const; | ||
size_t capacity_free() const; | ||
size_t usage() const; | ||
size_t size() const; | ||
bool lock_free() const; | ||
size_t tail() const { return tail_.load(); } | ||
size_t head() const { return head_.load(); } | ||
|
||
private: | ||
typedef char cache_line[64]; | ||
size_t increment(size_t idx) const { return (idx + 1) % kCapacity; } | ||
const size_t kSize; | ||
const size_t kCapacity; | ||
|
||
cache_line pad_storage_; | ||
std::vector<Element> array_; | ||
|
||
cache_line padtail_; | ||
std::atomic<size_t> tail_; | ||
cache_line padhead_; | ||
std::atomic<size_t> head_; // head(output) index | ||
cache_line padend_; | ||
}; | ||
|
||
template <typename Element> | ||
bool circular_fifo<Element>::push(Element& item) { | ||
const auto currenttail_ = tail_.load(std::memory_order_relaxed); | ||
const auto nexttail_ = increment(currenttail_); | ||
|
||
if (nexttail_ != head_.load(std::memory_order_acquire)) { | ||
array_[currenttail_] = std::move(item); | ||
tail_.store(nexttail_, std::memory_order_release); | ||
return true; | ||
} | ||
|
||
return false; // full queue | ||
} | ||
|
||
// Pop by Consumer can only update the head (load with relaxed, store with release) | ||
// the tail must be accessed with at least aquire | ||
template <typename Element> | ||
bool circular_fifo<Element>::pop(Element& item) { | ||
const auto currenthead_ = head_.load(std::memory_order_relaxed); | ||
if (currenthead_ == tail_.load(std::memory_order_acquire)) { | ||
return false; // empty queue | ||
} | ||
|
||
item = std::move(array_[currenthead_]); | ||
head_.store(increment(currenthead_), std::memory_order_release); | ||
return true; | ||
} | ||
|
||
template <typename Element> | ||
bool circular_fifo<Element>::empty() const { | ||
// snapshot with acceptance of that this comparison operation is not atomic | ||
return (head_.load(std::memory_order_relaxed) == tail_.load(std::memory_order_relaxed)); | ||
} | ||
|
||
// snapshot with acceptance that this comparison is not atomic | ||
template <typename Element> | ||
bool circular_fifo<Element>::full() const { | ||
const auto nexttail_ = increment(tail_.load(std::memory_order_relaxed)); // aquire, we dont know who call | ||
return (nexttail_ == head_.load(std::memory_order_relaxed)); | ||
} | ||
|
||
template <typename Element> | ||
bool circular_fifo<Element>::lock_free() const { | ||
return std::atomic<size_t>{}.is_lock_free(); | ||
} | ||
|
||
template <typename Element> | ||
size_t circular_fifo<Element>::size() const { | ||
return ((tail_.load() - head_.load() + kCapacity) % kCapacity); | ||
} | ||
|
||
template <typename Element> | ||
size_t circular_fifo<Element>::capacity_free() const { | ||
return (kCapacity - size() - 1); | ||
} | ||
|
||
template <typename Element> | ||
size_t circular_fifo<Element>::capacity() const { | ||
return kSize; | ||
} | ||
|
||
// percent usage | ||
template <typename Element> | ||
size_t circular_fifo<Element>::usage() const { | ||
return (100 * size() / kSize); | ||
} | ||
} // namespace spsc |