improve test cases (has bugs)

This commit is contained in:
mutouyun 2018-12-17 19:07:18 +08:00
parent 2c0e2ef1df
commit cca70b018c
10 changed files with 346 additions and 154 deletions

View File

@ -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); }
std::size_t connect(void) {
std::size_t connect() {
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);
}
std::size_t conn_count(void) const {
std::size_t conn_count() const {
return cc_.load(std::memory_order_acquire);
}
u2_t cursor(void) const {
u2_t cursor() const {
return wt_.load(std::memory_order_acquire);
}
auto acquire(void) {
auto acquire() {
while (lc_.exchange(1, std::memory_order_acquire)) {
std::this_thread::yield();
}
return index_of(wt_.load(std::memory_order_relaxed));
}
void commit(void) {
void commit() {
wt_.fetch_add(1, std::memory_order_relaxed);
lc_.store(0, std::memory_order_release);
}
@ -84,7 +84,7 @@ private:
};
elem_t block_[elem_max];
elem_t* elem_start(void) {
elem_t* elem_start() {
return block_;
}
@ -92,7 +92,7 @@ private:
elem_t* elem(u1_t i ) { return elem_start() + i; }
public:
elem_array(void) = default;
elem_array() = default;
elem_array(const elem_array&) = delete;
elem_array& operator=(const elem_array&) = delete;
@ -104,7 +104,7 @@ public:
using base_t::conn_count;
using base_t::cursor;
void* acquire(void) {
void* acquire() {
elem_t* el = elem(base_t::acquire());
// check all consumers have finished reading
while(1) {

View File

@ -23,7 +23,7 @@ private:
bool connected_ = false;
public:
queue(void) = default;
queue() = default;
explicit queue(array_t* arr) : queue() {
attach(arr);
@ -44,29 +44,29 @@ public:
return *this;
}
array_t * elems(void) {
array_t * elems() {
return elems_;
}
std::size_t connect(void) {
std::size_t connect() {
if (elems_ == nullptr) return error_count;
if (connected_) return error_count;
connected_ = true;
return elems_->connect();
}
std::size_t disconnect(void) {
std::size_t disconnect() {
if (elems_ == nullptr) return error_count;
if (!connected_) return error_count;
connected_ = false;
return elems_->disconnect();
}
std::size_t conn_count(void) const {
std::size_t conn_count() const {
return (elems_ == nullptr) ? error_count : elems_->conn_count();
}
bool connected(void) const {
bool connected() const {
return connected_;
}
@ -78,7 +78,7 @@ public:
return old;
}
array_t* detach(void) {
array_t* detach() {
if (elems_ == nullptr) return nullptr;
auto old = elems_;
elems_ = nullptr;
@ -113,7 +113,7 @@ public:
return true;
}
T pop(void) {
T pop() {
if (elems_ == nullptr) throw std::invalid_argument {
"This queue hasn't attached any elem_array."
};

View File

@ -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 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 buff_t recv(handle_t h);
class IPC_EXPORT channel {
public:
channel(void);
channel();
channel(char const * name);
channel(channel&& rhs);
~channel(void);
~channel();
void swap(channel& rhs);
channel& operator=(channel rhs);
bool valid(void) const;
char const * name (void) const;
bool valid() const;
char const * name () const;
channel clone(void) const;
channel clone() const;
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(buff_t const & buff);

View File

@ -16,24 +16,24 @@ IPC_EXPORT void close (void* mem);
class IPC_EXPORT handle {
public:
handle(void);
handle();
handle(char const * name, std::size_t size);
handle(handle&& rhs);
~handle(void);
~handle();
void swap(handle& rhs);
handle& operator=(handle rhs);
bool valid(void) const;
std::size_t size (void) const;
char const * name (void) const;
bool valid() const;
std::size_t size () const;
char const * name () const;
bool acquire(char const * name, std::size_t size);
void release(void);
void release();
void* get (void);
void close(void);
void* get ();
void close();
private:
class handle_;

View File

@ -90,15 +90,24 @@ handle_t connect(char const * name) {
if (mem == nullptr) {
return nullptr;
}
queue_t* queue;
{
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();
return h;
}
void disconnect(handle_t h) {
if (h == nullptr) {
return;
}
void* mem = nullptr;
{
std::unique_lock<rw_lock> guard { h2q_lc__ };
@ -112,6 +121,14 @@ void disconnect(handle_t h) {
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) {
if (data == nullptr) {
return false;
@ -196,7 +213,7 @@ public:
std::string n_;
};
channel::channel(void)
channel::channel()
: p_(p_->make()) {
}
@ -210,7 +227,7 @@ channel::channel(channel&& rhs)
swap(rhs);
}
channel::~channel(void) {
channel::~channel() {
p_->clear();
}
@ -223,15 +240,15 @@ channel& channel::operator=(channel rhs) {
return *this;
}
bool channel::valid(void) const {
bool channel::valid() const {
return (impl(p_)->h_ != nullptr);
}
char const * channel::name(void) const {
char const * channel::name() const {
return impl(p_)->n_.c_str();
}
channel channel::clone(void) const {
channel channel::clone() const {
return { name() };
}
@ -242,11 +259,14 @@ bool channel::connect(char const * name) {
return valid();
}
void channel::disconnect(void) {
if (!valid()) return;
void channel::disconnect() {
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) {
return ipc::send(impl(p_)->h_, data, size);
}

View File

@ -20,13 +20,13 @@ public:
handle_() = default;
handle_(handle* t) : t_{t} {}
~handle_(void) {
~handle_() {
t_->close();
t_->release();
}
};
handle::handle(void)
handle::handle()
: p_(p_->make(this)) {
}
@ -40,7 +40,7 @@ handle::handle(handle&& rhs)
swap(rhs);
}
handle::~handle(void) {
handle::~handle() {
p_->clear();
}
@ -53,15 +53,15 @@ handle& handle::operator=(handle rhs) {
return *this;
}
bool handle::valid(void) const {
bool handle::valid() const {
return impl(p_)->h_ != nullptr;
}
std::size_t handle::size(void) const {
std::size_t handle::size() const {
return impl(p_)->s_;
}
char const * handle::name(void) const {
char const * handle::name() const {
return impl(p_)->n_.c_str();
}
@ -73,7 +73,7 @@ bool handle::acquire(char const * name, std::size_t size) {
return valid();
}
void handle::release(void) {
void handle::release() {
if (!valid()) return;
shm::release(impl(p_)->h_, impl(p_)->s_);
impl(p_)->h_ = nullptr;
@ -81,7 +81,7 @@ void handle::release(void) {
impl(p_)->n_.clear();
}
void* handle::get(void) {
void* handle::get() {
if (!valid()) return nullptr;
if (impl(p_)->m_ == nullptr) {
return impl(p_)->m_ = shm::open(impl(p_)->h_);
@ -89,7 +89,7 @@ void* handle::get(void) {
else return impl(p_)->m_;
}
void handle::close(void) {
void handle::close() {
if (!valid()) return;
shm::close(impl(p_)->m_);
impl(p_)->m_ = nullptr;

52
test/random.hpp Normal file
View 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

View File

@ -4,6 +4,7 @@
#include <iostream>
#include <atomic>
#include <thread>
#include "stopwatch.hpp"
@ -38,3 +39,65 @@ struct test_stopwatch {
<< (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();
}

View File

@ -13,65 +13,16 @@
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>;
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 {
int pid_;
int dat_;
};
} // internal-linkage
template <bool V>
struct test_verify {
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>
struct test_cq<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>
void test_prod_cons(T* cq) {
test_cq<T> tcq { cq };
namespace {
std::thread producers[N];
std::thread consumers[M];
std::atomic_int fini { 0 };
class Unit : public TestSuite {
Q_OBJECT
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;
const char* name() const {
return "test_circ";
}
tcq.wait_start(M);
private slots:
void initTestCase();
void cleanupTestCase();
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 });
void test_inst();
void test_prod_cons_1v1();
void test_prod_cons_1v3();
void test_prod_cons_3v1();
void test_prod_cons_performance();
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>
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() {
@ -354,9 +299,9 @@ void Unit::test_queue() {
queue.attach(cq);
QVERIFY(queue.detach() != nullptr);
test_prod_cons<1, 3>((ipc::circ::queue<msg_t>*)nullptr);
test_prod_cons<3, 1>((ipc::circ::queue<msg_t>*)nullptr);
test_prod_cons<3, 3>((ipc::circ::queue<msg_t>*)nullptr);
benchmark_prod_cons<1, 3, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
benchmark_prod_cons<3, 1, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
benchmark_prod_cons<3, 3, LoopCount>((ipc::circ::queue<msg_t>*)nullptr);
}
} // internal-linkage

View File

@ -9,6 +9,9 @@
#include <string>
#include <cstring>
#include <algorithm>
#include <array>
#include <limits>
#include <utility>
#if defined(__GNUC__)
# include <cxxabi.h> // abi::__cxa_demangle
@ -18,10 +21,88 @@
#include "rw_lock.h"
#include "stopwatch.hpp"
#include "spin_lock.hpp"
#include "random.hpp"
#include "test.h"
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 {
Q_OBJECT
@ -30,13 +111,37 @@ class Unit : public TestSuite {
}
private slots:
void initTestCase();
void cleanupTestCase();
void test_rw_lock();
void test_send_recv();
void test_channel();
void test_channel_performance();
} unit__;
#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>
constexpr T acc(T b, T e) {
return (e + b) * (e - b + 1) / 2;
@ -235,4 +340,8 @@ void Unit::test_channel() {
t2.join();
}
void Unit::test_channel_performance() {
benchmark_prod_cons<1, 1, LoopCount, false>((ipc::channel*)nullptr);
}
} // internal-linkage