Skip to content

Commit

Permalink
rename file
Browse files Browse the repository at this point in the history
  • Loading branch information
KjellKod committed Jan 4, 2024
1 parent bb4dd4d commit 1fd2173
Showing 1 changed file with 140 additions and 0 deletions.
140 changes: 140 additions & 0 deletions src/q/spsc_circular_fifo.hpp
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

0 comments on commit 1fd2173

Please sign in to comment.