mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
improve test cases (has bugs)
This commit is contained in:
parent
2c0e2ef1df
commit
cca70b018c
@ -21,30 +21,30 @@ struct alignas(std::max_align_t) elem_array_head {
|
|||||||
|
|
||||||
static u1_t index_of(u2_t c) { return static_cast<u1_t>(c); }
|
static u1_t index_of(u2_t c) { return static_cast<u1_t>(c); }
|
||||||
|
|
||||||
std::size_t connect(void) {
|
std::size_t connect() {
|
||||||
return cc_.fetch_add(1, std::memory_order_release);
|
return cc_.fetch_add(1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t disconnect(void) {
|
std::size_t disconnect() {
|
||||||
return cc_.fetch_sub(1, std::memory_order_release);
|
return cc_.fetch_sub(1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t conn_count(void) const {
|
std::size_t conn_count() const {
|
||||||
return cc_.load(std::memory_order_acquire);
|
return cc_.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
u2_t cursor(void) const {
|
u2_t cursor() const {
|
||||||
return wt_.load(std::memory_order_acquire);
|
return wt_.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto acquire(void) {
|
auto acquire() {
|
||||||
while (lc_.exchange(1, std::memory_order_acquire)) {
|
while (lc_.exchange(1, std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
return index_of(wt_.load(std::memory_order_relaxed));
|
return index_of(wt_.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit(void) {
|
void commit() {
|
||||||
wt_.fetch_add(1, std::memory_order_relaxed);
|
wt_.fetch_add(1, std::memory_order_relaxed);
|
||||||
lc_.store(0, std::memory_order_release);
|
lc_.store(0, std::memory_order_release);
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ private:
|
|||||||
};
|
};
|
||||||
elem_t block_[elem_max];
|
elem_t block_[elem_max];
|
||||||
|
|
||||||
elem_t* elem_start(void) {
|
elem_t* elem_start() {
|
||||||
return block_;
|
return block_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ private:
|
|||||||
elem_t* elem(u1_t i ) { return elem_start() + i; }
|
elem_t* elem(u1_t i ) { return elem_start() + i; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
elem_array(void) = default;
|
elem_array() = default;
|
||||||
|
|
||||||
elem_array(const elem_array&) = delete;
|
elem_array(const elem_array&) = delete;
|
||||||
elem_array& operator=(const elem_array&) = delete;
|
elem_array& operator=(const elem_array&) = delete;
|
||||||
@ -104,7 +104,7 @@ public:
|
|||||||
using base_t::conn_count;
|
using base_t::conn_count;
|
||||||
using base_t::cursor;
|
using base_t::cursor;
|
||||||
|
|
||||||
void* acquire(void) {
|
void* acquire() {
|
||||||
elem_t* el = elem(base_t::acquire());
|
elem_t* el = elem(base_t::acquire());
|
||||||
// check all consumers have finished reading
|
// check all consumers have finished reading
|
||||||
while(1) {
|
while(1) {
|
||||||
|
|||||||
@ -23,7 +23,7 @@ private:
|
|||||||
bool connected_ = false;
|
bool connected_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue(void) = default;
|
queue() = default;
|
||||||
|
|
||||||
explicit queue(array_t* arr) : queue() {
|
explicit queue(array_t* arr) : queue() {
|
||||||
attach(arr);
|
attach(arr);
|
||||||
@ -44,29 +44,29 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
array_t * elems(void) {
|
array_t * elems() {
|
||||||
return elems_;
|
return elems_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t connect(void) {
|
std::size_t connect() {
|
||||||
if (elems_ == nullptr) return error_count;
|
if (elems_ == nullptr) return error_count;
|
||||||
if (connected_) return error_count;
|
if (connected_) return error_count;
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
return elems_->connect();
|
return elems_->connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t disconnect(void) {
|
std::size_t disconnect() {
|
||||||
if (elems_ == nullptr) return error_count;
|
if (elems_ == nullptr) return error_count;
|
||||||
if (!connected_) return error_count;
|
if (!connected_) return error_count;
|
||||||
connected_ = false;
|
connected_ = false;
|
||||||
return elems_->disconnect();
|
return elems_->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t conn_count(void) const {
|
std::size_t conn_count() const {
|
||||||
return (elems_ == nullptr) ? error_count : elems_->conn_count();
|
return (elems_ == nullptr) ? error_count : elems_->conn_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connected(void) const {
|
bool connected() const {
|
||||||
return connected_;
|
return connected_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ public:
|
|||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
array_t* detach(void) {
|
array_t* detach() {
|
||||||
if (elems_ == nullptr) return nullptr;
|
if (elems_ == nullptr) return nullptr;
|
||||||
auto old = elems_;
|
auto old = elems_;
|
||||||
elems_ = nullptr;
|
elems_ = nullptr;
|
||||||
@ -113,7 +113,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
T pop(void) {
|
T pop() {
|
||||||
if (elems_ == nullptr) throw std::invalid_argument {
|
if (elems_ == nullptr) throw std::invalid_argument {
|
||||||
"This queue hasn't attached any elem_array."
|
"This queue hasn't attached any elem_array."
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,27 +20,30 @@ buff_t make_buff(byte_t const (& data)[N]) { return make_buff(data, N); }
|
|||||||
IPC_EXPORT handle_t connect (char const * name);
|
IPC_EXPORT handle_t connect (char const * name);
|
||||||
IPC_EXPORT void disconnect(handle_t h);
|
IPC_EXPORT void disconnect(handle_t h);
|
||||||
|
|
||||||
|
IPC_EXPORT std::size_t conn_count(handle_t h);
|
||||||
|
|
||||||
IPC_EXPORT bool send(handle_t h, void const * data, std::size_t size);
|
IPC_EXPORT bool send(handle_t h, void const * data, std::size_t size);
|
||||||
IPC_EXPORT buff_t recv(handle_t h);
|
IPC_EXPORT buff_t recv(handle_t h);
|
||||||
|
|
||||||
class IPC_EXPORT channel {
|
class IPC_EXPORT channel {
|
||||||
public:
|
public:
|
||||||
channel(void);
|
channel();
|
||||||
channel(char const * name);
|
channel(char const * name);
|
||||||
channel(channel&& rhs);
|
channel(channel&& rhs);
|
||||||
|
|
||||||
~channel(void);
|
~channel();
|
||||||
|
|
||||||
void swap(channel& rhs);
|
void swap(channel& rhs);
|
||||||
channel& operator=(channel rhs);
|
channel& operator=(channel rhs);
|
||||||
|
|
||||||
bool valid(void) const;
|
bool valid() const;
|
||||||
char const * name (void) const;
|
char const * name () const;
|
||||||
|
|
||||||
channel clone(void) const;
|
channel clone() const;
|
||||||
|
|
||||||
bool connect(char const * name);
|
bool connect(char const * name);
|
||||||
void disconnect(void);
|
void disconnect();
|
||||||
|
std::size_t conn_count() const;
|
||||||
|
|
||||||
bool send(void const * data, std::size_t size);
|
bool send(void const * data, std::size_t size);
|
||||||
bool send(buff_t const & buff);
|
bool send(buff_t const & buff);
|
||||||
|
|||||||
@ -16,24 +16,24 @@ IPC_EXPORT void close (void* mem);
|
|||||||
|
|
||||||
class IPC_EXPORT handle {
|
class IPC_EXPORT handle {
|
||||||
public:
|
public:
|
||||||
handle(void);
|
handle();
|
||||||
handle(char const * name, std::size_t size);
|
handle(char const * name, std::size_t size);
|
||||||
handle(handle&& rhs);
|
handle(handle&& rhs);
|
||||||
|
|
||||||
~handle(void);
|
~handle();
|
||||||
|
|
||||||
void swap(handle& rhs);
|
void swap(handle& rhs);
|
||||||
handle& operator=(handle rhs);
|
handle& operator=(handle rhs);
|
||||||
|
|
||||||
bool valid(void) const;
|
bool valid() const;
|
||||||
std::size_t size (void) const;
|
std::size_t size () const;
|
||||||
char const * name (void) const;
|
char const * name () const;
|
||||||
|
|
||||||
bool acquire(char const * name, std::size_t size);
|
bool acquire(char const * name, std::size_t size);
|
||||||
void release(void);
|
void release();
|
||||||
|
|
||||||
void* get (void);
|
void* get ();
|
||||||
void close(void);
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class handle_;
|
class handle_;
|
||||||
|
|||||||
36
src/ipc.cpp
36
src/ipc.cpp
@ -90,15 +90,24 @@ handle_t connect(char const * name) {
|
|||||||
if (mem == nullptr) {
|
if (mem == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
queue_t* queue;
|
||||||
{
|
{
|
||||||
std::unique_lock<rw_lock> guard { h2q_lc__ };
|
std::unique_lock<rw_lock> guard { h2q_lc__ };
|
||||||
h2q__[h].attach(&(static_cast<shm_info_t*>(mem)->elems_));
|
queue = &(h2q__[h]);
|
||||||
|
if (queue == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
queue->attach(&(static_cast<shm_info_t*>(mem)->elems_));
|
||||||
|
queue->connect();
|
||||||
}
|
}
|
||||||
h_guard.release();
|
h_guard.release();
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(handle_t h) {
|
void disconnect(handle_t h) {
|
||||||
|
if (h == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void* mem = nullptr;
|
void* mem = nullptr;
|
||||||
{
|
{
|
||||||
std::unique_lock<rw_lock> guard { h2q_lc__ };
|
std::unique_lock<rw_lock> guard { h2q_lc__ };
|
||||||
@ -112,6 +121,14 @@ void disconnect(handle_t h) {
|
|||||||
shm::release(h, sizeof(queue_t));
|
shm::release(h, sizeof(queue_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t conn_count(handle_t h) {
|
||||||
|
auto queue = queue_of(h);
|
||||||
|
if (queue == nullptr) {
|
||||||
|
return error_count;
|
||||||
|
}
|
||||||
|
return queue->conn_count();
|
||||||
|
}
|
||||||
|
|
||||||
bool send(handle_t h, void const * data, std::size_t size) {
|
bool send(handle_t h, void const * data, std::size_t size) {
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@ -196,7 +213,7 @@ public:
|
|||||||
std::string n_;
|
std::string n_;
|
||||||
};
|
};
|
||||||
|
|
||||||
channel::channel(void)
|
channel::channel()
|
||||||
: p_(p_->make()) {
|
: p_(p_->make()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +227,7 @@ channel::channel(channel&& rhs)
|
|||||||
swap(rhs);
|
swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel::~channel(void) {
|
channel::~channel() {
|
||||||
p_->clear();
|
p_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,15 +240,15 @@ channel& channel::operator=(channel rhs) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool channel::valid(void) const {
|
bool channel::valid() const {
|
||||||
return (impl(p_)->h_ != nullptr);
|
return (impl(p_)->h_ != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
char const * channel::name(void) const {
|
char const * channel::name() const {
|
||||||
return impl(p_)->n_.c_str();
|
return impl(p_)->n_.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
channel channel::clone(void) const {
|
channel channel::clone() const {
|
||||||
return { name() };
|
return { name() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,11 +259,14 @@ bool channel::connect(char const * name) {
|
|||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void channel::disconnect(void) {
|
void channel::disconnect() {
|
||||||
if (!valid()) return;
|
|
||||||
ipc::disconnect(impl(p_)->h_);
|
ipc::disconnect(impl(p_)->h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t channel::conn_count() const {
|
||||||
|
return ipc::conn_count(impl(p_)->h_);
|
||||||
|
}
|
||||||
|
|
||||||
bool channel::send(void const *data, std::size_t size) {
|
bool channel::send(void const *data, std::size_t size) {
|
||||||
return ipc::send(impl(p_)->h_, data, size);
|
return ipc::send(impl(p_)->h_, data, size);
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/shm.cpp
18
src/shm.cpp
@ -20,13 +20,13 @@ public:
|
|||||||
handle_() = default;
|
handle_() = default;
|
||||||
handle_(handle* t) : t_{t} {}
|
handle_(handle* t) : t_{t} {}
|
||||||
|
|
||||||
~handle_(void) {
|
~handle_() {
|
||||||
t_->close();
|
t_->close();
|
||||||
t_->release();
|
t_->release();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handle::handle(void)
|
handle::handle()
|
||||||
: p_(p_->make(this)) {
|
: p_(p_->make(this)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ handle::handle(handle&& rhs)
|
|||||||
swap(rhs);
|
swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle::~handle(void) {
|
handle::~handle() {
|
||||||
p_->clear();
|
p_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,15 +53,15 @@ handle& handle::operator=(handle rhs) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle::valid(void) const {
|
bool handle::valid() const {
|
||||||
return impl(p_)->h_ != nullptr;
|
return impl(p_)->h_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t handle::size(void) const {
|
std::size_t handle::size() const {
|
||||||
return impl(p_)->s_;
|
return impl(p_)->s_;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const * handle::name(void) const {
|
char const * handle::name() const {
|
||||||
return impl(p_)->n_.c_str();
|
return impl(p_)->n_.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ bool handle::acquire(char const * name, std::size_t size) {
|
|||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle::release(void) {
|
void handle::release() {
|
||||||
if (!valid()) return;
|
if (!valid()) return;
|
||||||
shm::release(impl(p_)->h_, impl(p_)->s_);
|
shm::release(impl(p_)->h_, impl(p_)->s_);
|
||||||
impl(p_)->h_ = nullptr;
|
impl(p_)->h_ = nullptr;
|
||||||
@ -81,7 +81,7 @@ void handle::release(void) {
|
|||||||
impl(p_)->n_.clear();
|
impl(p_)->n_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* handle::get(void) {
|
void* handle::get() {
|
||||||
if (!valid()) return nullptr;
|
if (!valid()) return nullptr;
|
||||||
if (impl(p_)->m_ == nullptr) {
|
if (impl(p_)->m_ == nullptr) {
|
||||||
return impl(p_)->m_ = shm::open(impl(p_)->h_);
|
return impl(p_)->m_ = shm::open(impl(p_)->h_);
|
||||||
@ -89,7 +89,7 @@ void* handle::get(void) {
|
|||||||
else return impl(p_)->m_;
|
else return impl(p_)->m_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle::close(void) {
|
void handle::close() {
|
||||||
if (!valid()) return;
|
if (!valid()) return;
|
||||||
shm::close(impl(p_)->m_);
|
shm::close(impl(p_)->m_);
|
||||||
impl(p_)->m_ = nullptr;
|
impl(p_)->m_ = nullptr;
|
||||||
|
|||||||
52
test/random.hpp
Normal file
52
test/random.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
The Capo Library
|
||||||
|
Code covered by the MIT License
|
||||||
|
|
||||||
|
Author: mutouyun (http://orzz.org)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <random> // std::default_random_engine, std::uniform_int_distribution
|
||||||
|
#include <utility> // std::forward
|
||||||
|
|
||||||
|
namespace capo {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Simple way of generating random numbers
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <class Engine = std::default_random_engine,
|
||||||
|
class Distribution = std::uniform_int_distribution<>>
|
||||||
|
class random : public Distribution
|
||||||
|
{
|
||||||
|
using base_t = Distribution;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using engine_type = Engine;
|
||||||
|
using distribution_type = Distribution;
|
||||||
|
using result_type = typename distribution_type::result_type;
|
||||||
|
using param_type = typename distribution_type::param_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
engine_type engine_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename... T>
|
||||||
|
random(T&&... args)
|
||||||
|
: base_t (std::forward<T>(args)...)
|
||||||
|
, engine_(std::random_device{}())
|
||||||
|
{}
|
||||||
|
|
||||||
|
result_type operator()(void)
|
||||||
|
{
|
||||||
|
return base_t::operator()(engine_);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_type operator()(const param_type& parm)
|
||||||
|
{
|
||||||
|
return base_t::operator()(engine_, parm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace capo
|
||||||
63
test/test.h
63
test/test.h
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "stopwatch.hpp"
|
#include "stopwatch.hpp"
|
||||||
|
|
||||||
@ -38,3 +39,65 @@ struct test_stopwatch {
|
|||||||
<< (double(ts) / double(Loops * N)) << " us/d" << std::endl;
|
<< (double(ts) / double(Loops * N)) << " us/d" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <bool V>
|
||||||
|
struct test_verify;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct test_verify<false> {
|
||||||
|
test_verify (int) {}
|
||||||
|
void prepare (void*) {}
|
||||||
|
void push_data(int, ...) {}
|
||||||
|
void verify (int, int) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct test_cq;
|
||||||
|
|
||||||
|
template <int N, int M, int Loops, bool V = true, typename T>
|
||||||
|
void benchmark_prod_cons(T* cq) {
|
||||||
|
test_cq<T> tcq { cq };
|
||||||
|
|
||||||
|
std::thread producers[N];
|
||||||
|
std::thread consumers[M];
|
||||||
|
std::atomic_int fini { 0 };
|
||||||
|
|
||||||
|
test_stopwatch sw;
|
||||||
|
test_verify<V> vf { M };
|
||||||
|
|
||||||
|
int cid = 0;
|
||||||
|
for (auto& t : consumers) {
|
||||||
|
t = std::thread{[&, cid] {
|
||||||
|
vf.prepare(&t);
|
||||||
|
auto cn = tcq.connect();
|
||||||
|
|
||||||
|
using namespace std::placeholders;
|
||||||
|
tcq.recv(cn, std::bind(&test_verify<V>::push_data, &vf, cid, _1));
|
||||||
|
|
||||||
|
tcq.disconnect(cn);
|
||||||
|
if (++fini != std::extent<decltype(consumers)>::value) return;
|
||||||
|
sw.print_elapsed(N, M, Loops);
|
||||||
|
vf.verify(N, Loops);
|
||||||
|
}};
|
||||||
|
++cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcq.wait_start(M);
|
||||||
|
|
||||||
|
std::cout << "start producers..." << std::endl;
|
||||||
|
int pid = 0;
|
||||||
|
for (auto& t : producers) {
|
||||||
|
t = std::thread{[&, pid] {
|
||||||
|
sw.start();
|
||||||
|
for (int i = 0; i < Loops; ++i) {
|
||||||
|
tcq.send({ pid, i });
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
++pid;
|
||||||
|
}
|
||||||
|
for (auto& t : producers) t.join();
|
||||||
|
// quit
|
||||||
|
tcq.send({ -1, -1 });
|
||||||
|
|
||||||
|
for (auto& t : consumers) t.join();
|
||||||
|
}
|
||||||
|
|||||||
@ -13,65 +13,16 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
const char* name() const {
|
|
||||||
return "test_circ";
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void initTestCase();
|
|
||||||
void cleanupTestCase();
|
|
||||||
|
|
||||||
void test_inst();
|
|
||||||
void test_prod_cons_1v1();
|
|
||||||
void test_prod_cons_1v3();
|
|
||||||
void test_prod_cons_3v1();
|
|
||||||
void test_prod_cons_performance();
|
|
||||||
|
|
||||||
void test_queue();
|
|
||||||
} unit__;
|
|
||||||
|
|
||||||
#include "test_circ.moc"
|
|
||||||
|
|
||||||
using cq_t = ipc::circ::elem_array<12>;
|
using cq_t = ipc::circ::elem_array<12>;
|
||||||
cq_t* cq__;
|
cq_t* cq__;
|
||||||
|
|
||||||
constexpr int LoopCount = 1000000;
|
|
||||||
|
|
||||||
void Unit::initTestCase() {
|
|
||||||
TestSuite::initTestCase();
|
|
||||||
cq__ = new cq_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unit::cleanupTestCase() {
|
|
||||||
delete cq__;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unit::test_inst() {
|
|
||||||
std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl;
|
|
||||||
std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl;
|
|
||||||
std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl;
|
|
||||||
std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl;
|
|
||||||
|
|
||||||
QCOMPARE(static_cast<std::size_t>(cq_t::data_size) , static_cast<std::size_t>(12));
|
|
||||||
QCOMPARE(sizeof(cq_t), static_cast<std::size_t>(cq_t::block_size + cq_t::head_size));
|
|
||||||
|
|
||||||
std::cout << "sizeof(ipc::circ::elem_array<4096>) = " << sizeof(*cq__) << std::endl;
|
|
||||||
|
|
||||||
auto a = cq__->take(1);
|
|
||||||
auto b = cq__->take(2);
|
|
||||||
QCOMPARE(static_cast<std::size_t>(static_cast<ipc::byte_t*>(b) -
|
|
||||||
static_cast<ipc::byte_t*>(a)),
|
|
||||||
static_cast<std::size_t>(cq_t::elem_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msg_t {
|
struct msg_t {
|
||||||
int pid_;
|
int pid_;
|
||||||
int dat_;
|
int dat_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // internal-linkage
|
||||||
|
|
||||||
template <bool V>
|
template <bool V>
|
||||||
struct test_verify {
|
struct test_verify {
|
||||||
std::unordered_map<int, std::vector<int>>* list_;
|
std::unordered_map<int, std::vector<int>>* list_;
|
||||||
@ -112,17 +63,6 @@ struct test_verify {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_verify<false> {
|
|
||||||
test_verify (int) {}
|
|
||||||
void prepare (void*) {}
|
|
||||||
void push_data(int, msg_t const &) {}
|
|
||||||
void verify (int, int) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct test_cq;
|
|
||||||
|
|
||||||
template <std::size_t D>
|
template <std::size_t D>
|
||||||
struct test_cq<ipc::circ::elem_array<D>> {
|
struct test_cq<ipc::circ::elem_array<D>> {
|
||||||
using ca_t = ipc::circ::elem_array<D>;
|
using ca_t = ipc::circ::elem_array<D>;
|
||||||
@ -217,57 +157,62 @@ struct test_cq<ipc::circ::queue<T>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int N, int M, bool V = true, int Loops = LoopCount, typename T>
|
namespace {
|
||||||
void test_prod_cons(T* cq) {
|
|
||||||
test_cq<T> tcq { cq };
|
|
||||||
|
|
||||||
std::thread producers[N];
|
class Unit : public TestSuite {
|
||||||
std::thread consumers[M];
|
Q_OBJECT
|
||||||
std::atomic_int fini { 0 };
|
|
||||||
|
|
||||||
test_stopwatch sw;
|
const char* name() const {
|
||||||
test_verify<V> vf { M };
|
return "test_circ";
|
||||||
|
|
||||||
int cid = 0;
|
|
||||||
for (auto& t : consumers) {
|
|
||||||
t = std::thread{[&, cid] {
|
|
||||||
vf.prepare(&t);
|
|
||||||
auto cn = tcq.connect();
|
|
||||||
|
|
||||||
using namespace std::placeholders;
|
|
||||||
tcq.recv(cn, std::bind(&test_verify<V>::push_data, &vf, cid, _1));
|
|
||||||
|
|
||||||
tcq.disconnect(cn);
|
|
||||||
if (++fini != std::extent<decltype(consumers)>::value) return;
|
|
||||||
sw.print_elapsed(N, M, Loops);
|
|
||||||
vf.verify(N, Loops);
|
|
||||||
}};
|
|
||||||
++cid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tcq.wait_start(M);
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
std::cout << "start producers..." << std::endl;
|
void test_inst();
|
||||||
int pid = 0;
|
void test_prod_cons_1v1();
|
||||||
for (auto& t : producers) {
|
void test_prod_cons_1v3();
|
||||||
t = std::thread{[&, pid] {
|
void test_prod_cons_3v1();
|
||||||
sw.start();
|
void test_prod_cons_performance();
|
||||||
for (int i = 0; i < Loops; ++i) {
|
|
||||||
tcq.send({ pid, i });
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
++pid;
|
|
||||||
}
|
|
||||||
for (auto& t : producers) t.join();
|
|
||||||
// quit
|
|
||||||
tcq.send({ -1, -1 });
|
|
||||||
|
|
||||||
for (auto& t : consumers) t.join();
|
void test_queue();
|
||||||
|
} unit__;
|
||||||
|
|
||||||
|
#include "test_circ.moc"
|
||||||
|
|
||||||
|
constexpr int LoopCount = 1000000;
|
||||||
|
|
||||||
|
void Unit::initTestCase() {
|
||||||
|
TestSuite::initTestCase();
|
||||||
|
cq__ = new cq_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unit::cleanupTestCase() {
|
||||||
|
delete cq__;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unit::test_inst() {
|
||||||
|
std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl;
|
||||||
|
std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl;
|
||||||
|
std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl;
|
||||||
|
std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl;
|
||||||
|
|
||||||
|
QCOMPARE(static_cast<std::size_t>(cq_t::data_size) , static_cast<std::size_t>(12));
|
||||||
|
QCOMPARE(sizeof(cq_t), static_cast<std::size_t>(cq_t::block_size + cq_t::head_size));
|
||||||
|
|
||||||
|
std::cout << "sizeof(ipc::circ::elem_array<4096>) = " << sizeof(*cq__) << std::endl;
|
||||||
|
|
||||||
|
auto a = cq__->take(1);
|
||||||
|
auto b = cq__->take(2);
|
||||||
|
QCOMPARE(static_cast<std::size_t>(static_cast<ipc::byte_t*>(b) -
|
||||||
|
static_cast<ipc::byte_t*>(a)),
|
||||||
|
static_cast<std::size_t>(cq_t::elem_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N, int M, bool V = true, int Loops = LoopCount>
|
template <int N, int M, bool V = true, int Loops = LoopCount>
|
||||||
void test_prod_cons() {
|
void test_prod_cons() {
|
||||||
test_prod_cons<N, M, V, Loops>(cq__);
|
benchmark_prod_cons<N, M, Loops, V>(cq__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_prod_cons_1v1() {
|
void Unit::test_prod_cons_1v1() {
|
||||||
@ -354,9 +299,9 @@ void Unit::test_queue() {
|
|||||||
queue.attach(cq);
|
queue.attach(cq);
|
||||||
QVERIFY(queue.detach() != nullptr);
|
QVERIFY(queue.detach() != nullptr);
|
||||||
|
|
||||||
test_prod_cons<1, 3>((ipc::circ::queue<msg_t>*)nullptr);
|
benchmark_prod_cons<1, 3, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
|
||||||
test_prod_cons<3, 1>((ipc::circ::queue<msg_t>*)nullptr);
|
benchmark_prod_cons<3, 1, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
|
||||||
test_prod_cons<3, 3>((ipc::circ::queue<msg_t>*)nullptr);
|
benchmark_prod_cons<3, 3, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
@ -9,6 +9,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
# include <cxxabi.h> // abi::__cxa_demangle
|
# include <cxxabi.h> // abi::__cxa_demangle
|
||||||
@ -18,10 +21,88 @@
|
|||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "stopwatch.hpp"
|
#include "stopwatch.hpp"
|
||||||
#include "spin_lock.hpp"
|
#include "spin_lock.hpp"
|
||||||
|
#include "random.hpp"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
std::vector<ipc::buff_t> datas__;
|
||||||
|
|
||||||
|
constexpr int DataMin = 2;
|
||||||
|
constexpr int DataMax = 512;
|
||||||
|
constexpr int LoopCount = 100/*0*//*000*/;
|
||||||
|
|
||||||
|
} // internal-linkage
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct test_cq<ipc::channel> {
|
||||||
|
using cn_t = ipc::channel;
|
||||||
|
|
||||||
|
std::string conn_name_;
|
||||||
|
std::size_t conn_count_ = 0;
|
||||||
|
|
||||||
|
test_cq(void*)
|
||||||
|
: conn_name_("test-ipc-channel")
|
||||||
|
{}
|
||||||
|
|
||||||
|
cn_t connect() {
|
||||||
|
cn_t cn { conn_name_.c_str() };
|
||||||
|
conn_count_ = cn.conn_count();
|
||||||
|
return cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect(cn_t&) {}
|
||||||
|
|
||||||
|
void wait_start(int M) {
|
||||||
|
while (conn_count_ != static_cast<std::size_t>(M)) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void recv(cn_t& cn, F&& proc) {
|
||||||
|
do {
|
||||||
|
auto msg = cn.recv();
|
||||||
|
if (msg.size() < 2) return;
|
||||||
|
proc(msg);
|
||||||
|
} while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(const std::array<int, 2>& info) {
|
||||||
|
thread_local auto cn = connect();
|
||||||
|
int n = info[1];
|
||||||
|
if (n < 0) {
|
||||||
|
cn.send(ipc::buff_t { '\0' });
|
||||||
|
}
|
||||||
|
else cn.send(datas__[n]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//template <>
|
||||||
|
//struct test_verify<true> {
|
||||||
|
// std::unordered_map<int, std::vector<ipc::buff_t>> list_;
|
||||||
|
// int lcount_;
|
||||||
|
|
||||||
|
// test_verify(int M) : lcount_{ M } {}
|
||||||
|
|
||||||
|
// void prepare(void* pt) {
|
||||||
|
// std::cout << "start consumer: " << pt << std::endl;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void push_data(int cid, ipc::buff_t const & msg) {
|
||||||
|
// list_[cid].emplace_back(std::move(msg));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void verify(int /*N*/, int /*Loops*/) {
|
||||||
|
// std::cout << "verifying..." << std::endl;
|
||||||
|
// for (int m = 0; m < lcount_; ++m) {
|
||||||
|
// QCOMPARE(datas__, list_[m]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -30,13 +111,37 @@ class Unit : public TestSuite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
void test_rw_lock();
|
void test_rw_lock();
|
||||||
void test_send_recv();
|
void test_send_recv();
|
||||||
void test_channel();
|
void test_channel();
|
||||||
|
void test_channel_performance();
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_ipc.moc"
|
#include "test_ipc.moc"
|
||||||
|
|
||||||
|
void Unit::initTestCase() {
|
||||||
|
TestSuite::initTestCase();
|
||||||
|
|
||||||
|
capo::random<> rdm { DataMin, DataMax };
|
||||||
|
capo::random<> bit { 0, (std::numeric_limits<ipc::buff_t::value_type>::max)() };
|
||||||
|
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
auto n = rdm();
|
||||||
|
ipc::buff_t buff(static_cast<ipc::buff_t::size_type>(n));
|
||||||
|
for (std::size_t k = 0; k < buff.size(); ++k) {
|
||||||
|
buff[k] = static_cast<ipc::buff_t::value_type>(bit());
|
||||||
|
}
|
||||||
|
datas__.emplace_back(std::move(buff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unit::cleanupTestCase() {
|
||||||
|
datas__.clear();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr T acc(T b, T e) {
|
constexpr T acc(T b, T e) {
|
||||||
return (e + b) * (e - b + 1) / 2;
|
return (e + b) * (e - b + 1) / 2;
|
||||||
@ -235,4 +340,8 @@ void Unit::test_channel() {
|
|||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Unit::test_channel_performance() {
|
||||||
|
benchmark_prod_cons<1, 1, LoopCount, false>((ipc::channel*)nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user