#include #include #include #include #include #include #include #include #include "circ_elem_array.h" #include "circ_queue.h" #include "test.h" 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(cq_t::data_size) , static_cast(12)); QCOMPARE(sizeof(cq_t), static_cast(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(static_cast(b) - static_cast(a)), static_cast(cq_t::elem_size)); } struct msg_t { int pid_; int dat_; }; template struct test_verify { std::unordered_map>* list_; int lcount_; test_verify(int M) { list_ = new std::remove_reference_t[ static_cast(lcount_ = M) ]; } ~test_verify() { delete [] list_; } void prepare(void* pt) { std::cout << "start consumer: " << pt << std::endl; } void push_data(int cid, msg_t const & msg) { list_[cid][msg.pid_].push_back(msg.dat_); } void verify(int N, int Loops) { std::cout << "verifying..." << std::endl; for (int m = 0; m < lcount_; ++m) { auto& cons_vec = list_[m]; for (int n = 0; n < N; ++n) { auto& vec = cons_vec[n]; QCOMPARE(vec.size(), static_cast(Loops)); int i = 0; for (int d : vec) { QCOMPARE(i, d); ++i; } } } } }; template <> struct test_verify { test_verify (int) {} void prepare (void*) {} void push_data(int, msg_t const &) {} void verify (int, int) {} }; template struct test_cq; template struct test_cq> { using ca_t = ipc::circ::elem_array; using cn_t = typename ca_t::u2_t; ca_t* ca_; test_cq(ca_t* ca) : ca_(ca) { ::new (ca) ca_t; } cn_t connect() { auto cur = ca_->cursor(); ca_->connect(); return cur; } void disconnect(cn_t) { ca_->disconnect(); } void wait_start(int M) { while (ca_->conn_count() != static_cast(M)) { std::this_thread::yield(); } } template void recv(cn_t cur, F&& proc) { do { while (cur != ca_->cursor()) { msg_t* pmsg = static_cast(ca_->take(cur)), msg = *pmsg; ca_->put(pmsg); if (msg.pid_ < 0) return; ++cur; proc(msg); } std::this_thread::yield(); } while(1); } void send(msg_t const & msg) { msg_t* pmsg = static_cast(ca_->acquire()); (*pmsg) = msg; ca_->commit(pmsg); } }; template struct test_cq> { using cn_t = ipc::circ::queue; using ca_t = typename cn_t::array_t; ca_t* ca_; test_cq(void*) : ca_(reinterpret_cast(cq__)) { ::new (ca_) ca_t; } cn_t connect() { cn_t queue; [&] { queue.attach(ca_); QVERIFY(queue.connect() != ipc::error_count); } (); return queue; } void disconnect(cn_t& queue) { QVERIFY(queue.disconnect() != ipc::error_count); QVERIFY(queue.detach() != nullptr); } void wait_start(int M) { while (ca_->conn_count() != static_cast(M)) { std::this_thread::yield(); } } template void recv(cn_t& queue, F&& proc) { do { auto msg = queue.pop(); if (msg.pid_ < 0) return; proc(msg); } while(1); } void send(msg_t const & msg) { cn_t{ ca_ }.push(msg); } }; template void test_prod_cons(T* cq) { test_cq tcq { cq }; std::thread producers[N]; std::thread consumers[M]; std::atomic_int fini { 0 }; test_stopwatch sw; test_verify 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::push_data, &vf, cid, _1)); tcq.disconnect(cn); if (++fini != std::extent::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(); } template void test_prod_cons() { test_prod_cons(cq__); } void Unit::test_prod_cons_1v1() { test_prod_cons<1, 1>(); } void Unit::test_prod_cons_1v3() { test_prod_cons<1, 3>(); } void Unit::test_prod_cons_3v1() { test_prod_cons<3, 1>(); } template struct test_performance { static void start() { test_performance

::start(); test_prod_cons(); } }; template struct test_performance<1, C> { static void start() { test_performance<1, C - 1>::start(); test_prod_cons<1, C, false>(); } }; template struct test_performance { static void start() { test_performance

::start(); test_prod_cons(); } }; template <> struct test_performance<1, 1> { static void start() { test_prod_cons<1, 1, false>(); } }; void Unit::test_prod_cons_performance() { test_performance<1 , 10>::start(); test_performance<10, 1 >::start(); test_performance<10, 10>::start(); test_prod_cons <3 , 3 >(); // test & verify } #ifndef QVERIFY_EXCEPTION_THROWN #define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \ do {\ QT_TRY {\ QT_TRY {\ expression;\ QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \ " but no exception caught", __FILE__, __LINE__);\ return;\ } QT_CATCH (const exceptiontype &) {\ }\ } QT_CATCH (const std::exception &e) {\ QByteArray msg = QByteArray() + "Expected exception of type " #exceptiontype \ " to be thrown but std::exception caught with message: " + e.what(); \ QTest::qFail(msg.constData(), __FILE__, __LINE__);\ return;\ } QT_CATCH (...) {\ QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \ " but unknown exception caught", __FILE__, __LINE__);\ return;\ }\ } while (false) #endif/*!QVERIFY_EXCEPTION_THROWN*/ void Unit::test_queue() { ipc::circ::queue queue; queue.push(1, 2); QVERIFY_EXCEPTION_THROWN(queue.pop(), std::exception); QVERIFY(sizeof(decltype(queue)::array_t) <= sizeof(*cq__)); auto cq = ::new (cq__) decltype(queue)::array_t; queue.attach(cq); QVERIFY(queue.detach() != nullptr); test_prod_cons<1, 3>((ipc::circ::queue*)nullptr); test_prod_cons<3, 1>((ipc::circ::queue*)nullptr); test_prod_cons<3, 3>((ipc::circ::queue*)nullptr); } } // internal-linkage