diff --git a/build/src.pro b/build/src.pro index 0d45823..faffb18 100644 --- a/build/src.pro +++ b/build/src.pro @@ -15,7 +15,7 @@ INCLUDEPATH += \ HEADERS += \ ../include/export.h \ ../include/shm.h \ - ../test/test.h + ../src/circ_queue.h SOURCES += \ ../src/shm.cpp diff --git a/build/test.pro b/build/test.pro index 8cfe5d7..fe568b6 100644 --- a/build/test.pro +++ b/build/test.pro @@ -16,6 +16,7 @@ HEADERS += \ SOURCES += \ ../test/main.cpp \ - ../test/test_shm.cpp + ../test/test_shm.cpp \ + ../test/test_circ_queue.cpp LIBS += -L$${DESTDIR} -lipc diff --git a/include/export.h b/include/export.h index 962708b..ef23d5d 100644 --- a/include/export.h +++ b/include/export.h @@ -9,7 +9,7 @@ /* * Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT. - * Not using QtCore cause it shouldn't depend on Qt & C++. + * Not using QtCore cause it shouldn't depend on Qt. */ #if defined(_MSC_VER) diff --git a/src/circ_queue.cpp b/src/circ_queue.cpp new file mode 100644 index 0000000..465db5f --- /dev/null +++ b/src/circ_queue.cpp @@ -0,0 +1,101 @@ + +#include "circ_queue.h" + +namespace ipc { + +class circ_queue_ { +public: +}; + +circ_queue::circ_queue(void) + : p_(new circ_queue_) { +} + +circ_queue::circ_queue(void* mem, std::size_t size) + : circ_queue() { + attach(mem, size); +} + +circ_queue::circ_queue(circ_queue&& rhs) + : circ_queue() { + swap(rhs); +} + +circ_queue::~circ_queue(void) { + delete p_; +} + +void circ_queue::swap(circ_queue& rhs) { + std::swap(p_, rhs.p_); +} + +circ_queue& circ_queue::operator=(circ_queue rhs) { + swap(rhs); + return *this; +} + +bool circ_queue::valid(void) const { + return (p_ != nullptr) && (p_->elem_start_ != nullptr) && (p_->elem_size_ != 0); +} + +std::size_t circ_queue::head_size(void) const { + if (!valid()) return 0; + return reinterpret_cast(p_->start_) - p_->elem_start_; +} + +std::size_t circ_queue::elem_size(void) const { + if (!valid()) return 0; + return p_->elem_size_; +} + +bool circ_queue::attach(void* mem, std::size_t size) { + if (p_ == nullptr) return false; + if ((mem == nullptr) || (size == 0)) return false; + if (size < p_->size_min) return false; + + detach(); + + p_->start_ = static_cast(mem); + p_->count_ = p_->start_ + 1; + p_->flag_ = reinterpret_cast(p_->count_ + 1); + + std::size_t hs = std::max(size / (p_->elem_max + 1), static_cast(p_->head_size)); + p_->elem_start_ = reinterpret_cast(p_->start_) + hs; + p_->elem_size_ = (size - hs) / p_->elem_max; + + p_->lc_start_ = p_->start_->load(); + p_->lc_count_ = p_->count_->load(); + return true; +} + +void circ_queue::detach(void) { + if (!valid()) return; + p_->elem_start_ = nullptr; + p_->elem_size_ = 0; + p_->start_ = nullptr; + p_->count_ = nullptr; + p_->lc_start_ = 0; + p_->lc_count_ = 0; +} + +void* circ_queue::alloc(void) { + +} + +bool circ_queue::push(void* elem) { + if (!valid()) return false; + if (elem == nullptr) return false; + + do { + std::uint8_t offset = p_->start_->load() + p_->count_->load(); // overflow is mod + std::size_t lastps = offset * p_->elem_size_; + if (p_->flag_->test_and_set(std::memory_order_acquire)) { + continue; + } + memcpy(p_->elem_start_ + lastps, elem, p_->elem_size_); + } while(1); + + return true; +} + +} // namespace ipc diff --git a/src/circ_queue.h b/src/circ_queue.h new file mode 100644 index 0000000..b7a9303 --- /dev/null +++ b/src/circ_queue.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace ipc { + +struct circ_queue_head { + using u8_t = std::atomic; + + u8_t cc_; // connection count + u8_t rd_; // read-index + u8_t wt_; // write-index +}; + +template +class circ_queue : public circ_queue_head { +public: + enum : std::size_t { + total_size = Size, + head_size = sizeof(circ_queue_head), + block_size = Size - head_size, + elem_max = std::numeric_limits::max(), + elem_size = Size / (elem_max + 1) + }; + + static_assert(Size > head_size , "Size must > head_size"); + static_assert(elem_size >= head_size, "elem_size must >= head_size"); + static_assert(Size % elem_size == 0 , "Size must be multiple of elem_size"); + + circ_queue(void) = default; + ~circ_queue(void) = delete; + + circ_queue(const circ_queue&) = delete; + circ_queue& operator=(const circ_queue&) = delete; + circ_queue(circ_queue&&) = delete; + circ_queue& operator=(circ_queue&&) = delete; + + std::size_t connect(void) { + return cc_.fetch_add(1, std::memory_order_relaxed); + } + + std::size_t disconnect(void) { + return cc_.fetch_sub(1, std::memory_order_relaxed); + } + + std::tuple acquire(void) { + std::uint8_t wt_id = wt_.fetch_add(1, std::memory_order_relaxed); + return std::make_tuple(&(block_[wt_id]), wt_id); + } + + void commit() { + + } + +private: + std::uint8_t block_[block_size]; +}; + +} // namespace ipc diff --git a/test/main.cpp b/test/main.cpp index f55234a..4d5d5ac 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -6,12 +6,16 @@ namespace { -QVector suites__; +QVector* suites__ = nullptr; } // internal-linkage TestSuite::TestSuite(void) { - suites__ << this; + static struct __ { + QVector suites_; + __(void) { suites__ = &suites_; } + } _; + _.suites_ << this; } int main(int argc, char* argv[]) { @@ -19,7 +23,7 @@ int main(int argc, char* argv[]) { Q_UNUSED(app) int failed_count = 0; - for (const auto& suite : suites__) { + for (const auto& suite : (*suites__)) { if (QTest::qExec(suite, argc, argv) != 0) ++failed_count; } diff --git a/test/test_circ_queue.cpp b/test/test_circ_queue.cpp new file mode 100644 index 0000000..410edab --- /dev/null +++ b/test/test_circ_queue.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include "circ_queue.h" +#include "test.h" + +namespace { + +class Unit : public TestSuite { + Q_OBJECT + +private slots: + void test_(void); +} unit__; + +#include "test_circ_queue.moc" + +void Unit::test_(void) { + auto* cq = new ipc::circ_queue<4096>; + QCOMPARE(sizeof(*cq), static_cast(4096)); +} + +} // internal-linkage