fiber/test/test_fiber_post.cpp
2018-02-09 17:30:52 +01:00

441 lines
11 KiB
C++

// Copyright Oliver Kowalke 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// This test is based on the tests of Boost.Thread
#include <chrono>
#include <mutex>
#include <sstream>
#include <string>
#include <boost/assert.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/fiber/all.hpp>
int value1 = 0;
std::string value2 = "";
struct X {
int value;
void foo( int i) {
value = i;
}
};
class copyable {
public:
bool state;
int value;
copyable() :
state( false),
value( -1) {
}
copyable( int v) :
state( true),
value( v) {
}
void operator()() {
value1 = value;
}
};
class moveable {
public:
bool state;
int value;
moveable() :
state( false),
value( -1) {
}
moveable( int v) :
state( true),
value( v) {
}
moveable( moveable && other) :
state( other.state),
value( other.value) {
other.state = false;
other.value = -1;
}
moveable & operator=( moveable && other) {
if ( this == & other) return * this;
state = other.state;
value = other.value;
other.state = false;
other.value = -1;
return * this;
}
moveable( moveable const& other) = delete;
moveable & operator=( moveable const& other) = delete;
void operator()() {
value1 = value;
}
};
class detachable {
private:
int alive_count_;
public:
static int alive_count;
static bool was_running;
detachable() :
alive_count_( 1) {
++alive_count;
}
detachable( detachable const& g) :
alive_count_( g.alive_count_) {
++alive_count;
}
~detachable() {
alive_count_ = 0;
--alive_count;
}
void operator()() {
BOOST_CHECK_EQUAL(1, alive_count_);
was_running = true;
}
};
int detachable::alive_count = 0;
bool detachable::was_running = false;
void fn1() {
value1 = 1;
}
void fn2( int i, std::string const& s) {
value1 = i;
value2 = s;
}
void fn3( int & i) {
i = 1;
boost::this_fiber::yield();
i = 1;
boost::this_fiber::yield();
i = 2;
boost::this_fiber::yield();
i = 3;
boost::this_fiber::yield();
i = 5;
boost::this_fiber::yield();
i = 8;
}
void fn4() {
boost::this_fiber::yield();
}
void fn5() {
boost::fibers::fiber f( boost::fibers::launch::post, fn4);
BOOST_CHECK( f.joinable() );
f.join();
BOOST_CHECK( ! f.joinable() );
}
void test_scheduler_dtor() {
boost::fibers::context * ctx(
boost::fibers::context::active() );
(void)ctx;
}
void test_join_fn() {
{
value1 = 0;
boost::fibers::fiber f( boost::fibers::launch::post, fn1);
f.join();
BOOST_CHECK_EQUAL( value1, 1);
}
{
value1 = 0;
value2 = "";
boost::fibers::fiber f( boost::fibers::launch::post, fn2, 3, "abc");
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
}
void test_join_memfn() {
X x = {0};
BOOST_CHECK_EQUAL( x.value, 0);
boost::fibers::fiber( boost::fibers::launch::post, & X::foo, & x, 3).join();
BOOST_CHECK_EQUAL( x.value, 3);
}
void test_join_copyable() {
value1 = 0;
copyable cp( 3);
BOOST_CHECK( cp.state);
BOOST_CHECK_EQUAL( value1, 0);
boost::fibers::fiber f( boost::fibers::launch::post, cp);
f.join();
BOOST_CHECK( cp.state);
BOOST_CHECK_EQUAL( value1, 3);
}
void test_join_moveable() {
value1 = 0;
moveable mv( 7);
BOOST_CHECK( mv.state);
BOOST_CHECK_EQUAL( value1, 0);
boost::fibers::fiber f( boost::fibers::launch::post, std::move( mv) );
f.join();
BOOST_CHECK( ! mv.state);
BOOST_CHECK_EQUAL( value1, 7);
}
void test_join_lambda() {
{
value1 = 0;
value2 = "";
int i = 3;
std::string abc("abc");
boost::fibers::fiber f(
boost::fibers::launch::post, [i,abc]() {
value1 = i;
value2 = abc;
});
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
{
value1 = 0;
value2 = "";
int i = 3;
std::string abc("abc");
boost::fibers::fiber f(
boost::fibers::launch::post, [](int i, std::string const& abc) {
value1 = i;
value2 = abc;
},
i, abc);
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
}
void test_join_bind() {
{
value1 = 0;
value2 = "";
int i = 3;
std::string abc("abc");
boost::fibers::fiber f(
boost::fibers::launch::post, std::bind(
[i,abc]() {
value1 = i;
value2 = abc;
}
));
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
{
value1 = 0;
value2 = "";
std::string abc("abc");
boost::fibers::fiber f(
boost::fibers::launch::post, std::bind(
[](std::string & str) {
value1 = 3;
value2 = str;
},
abc
));
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
{
value1 = 0;
value2 = "";
std::string abc("abc");
boost::fibers::fiber f(
boost::fibers::launch::post, std::bind(
[]( std::string & str) {
value1 = 3;
value2 = str;
},
std::placeholders::_1
),
std::ref( abc) );
f.join();
BOOST_CHECK_EQUAL( value1, 3);
BOOST_CHECK_EQUAL( value2, "abc");
}
}
void test_join_in_fiber() {
// spawn fiber f
// f spawns an new fiber f' in its fiber-fn
// f' yields in its fiber-fn
// f joins s' and gets suspended (waiting on s')
boost::fibers::fiber f( boost::fibers::launch::post, fn5);
BOOST_CHECK( f.joinable() );
// join() resumes f + f' which completes
f.join();
BOOST_CHECK( ! f.joinable() );
}
void test_move_fiber() {
boost::fibers::fiber f1;
BOOST_CHECK( ! f1.joinable() );
boost::fibers::fiber f2( boost::fibers::launch::post, fn1);
BOOST_CHECK( f2.joinable() );
f1 = std::move( f2);
BOOST_CHECK( f1.joinable() );
BOOST_CHECK( ! f2.joinable() );
f1.join();
BOOST_CHECK( ! f1.joinable() );
BOOST_CHECK( ! f2.joinable() );
}
void test_id() {
boost::fibers::fiber f1;
boost::fibers::fiber f2( boost::fibers::launch::post, fn1);
BOOST_CHECK( ! f1.joinable() );
BOOST_CHECK( f2.joinable() );
BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f1.get_id() );
BOOST_CHECK( boost::fibers::fiber::id() != f2.get_id() );
boost::fibers::fiber f3( boost::fibers::launch::post, fn1);
BOOST_CHECK( f2.get_id() != f3.get_id() );
f1 = std::move( f2);
BOOST_CHECK( f1.joinable() );
BOOST_CHECK( ! f2.joinable() );
BOOST_CHECK( boost::fibers::fiber::id() != f1.get_id() );
BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f2.get_id() );
BOOST_CHECK( ! f2.joinable() );
f1.join();
f3.join();
}
void test_yield() {
int v1 = 0, v2 = 0;
BOOST_CHECK_EQUAL( 0, v1);
BOOST_CHECK_EQUAL( 0, v2);
boost::fibers::fiber f1( boost::fibers::launch::post, fn3, std::ref( v1) );
boost::fibers::fiber f2( boost::fibers::launch::post, fn3, std::ref( v2) );
f1.join();
f2.join();
BOOST_CHECK( ! f1.joinable() );
BOOST_CHECK( ! f2.joinable() );
BOOST_CHECK_EQUAL( 8, v1);
BOOST_CHECK_EQUAL( 8, v2);
}
void test_sleep_for() {
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
std::chrono::milliseconds ms(500);
time_point t0 = Clock::now();
boost::this_fiber::sleep_for(ms);
time_point t1 = Clock::now();
std::chrono::nanoseconds ns = (t1 - t0) - ms;
std::chrono::nanoseconds err = ms / 10;
// This test is spurious as it depends on the time the fiber system switches the fiber
BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count());
}
void test_sleep_until() {
{
typedef std::chrono::steady_clock Clock;
typedef Clock::time_point time_point;
std::chrono::milliseconds ms(500);
time_point t0 = Clock::now();
boost::this_fiber::sleep_until(t0 + ms);
time_point t1 = Clock::now();
std::chrono::nanoseconds ns = (t1 - t0) - ms;
std::chrono::nanoseconds err = ms / 10;
// This test is spurious as it depends on the time the thread system switches the threads
BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count());
}
{
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
std::chrono::milliseconds ms(500);
time_point t0 = Clock::now();
boost::this_fiber::sleep_until(t0 + ms);
time_point t1 = Clock::now();
std::chrono::nanoseconds ns = (t1 - t0) - ms;
std::chrono::nanoseconds err = ms / 10;
// This test is spurious as it depends on the time the thread system switches the threads
BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count());
}
}
void do_wait( boost::fibers::barrier* b) {
b->wait();
}
void test_detach() {
{
boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) );
BOOST_CHECK( f.joinable() );
f.detach();
BOOST_CHECK( ! f.joinable() );
boost::this_fiber::sleep_for( std::chrono::milliseconds(250) );
BOOST_CHECK( detachable::was_running);
BOOST_CHECK_EQUAL( 0, detachable::alive_count);
}
{
boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) );
BOOST_CHECK( f.joinable() );
boost::this_fiber::yield();
f.detach();
BOOST_CHECK( ! f.joinable() );
boost::this_fiber::sleep_for( std::chrono::milliseconds(250) );
BOOST_CHECK( detachable::was_running);
BOOST_CHECK_EQUAL( 0, detachable::alive_count);
}
}
boost::unit_test::test_suite * init_unit_test_suite( int, char* []) {
boost::unit_test::test_suite * test =
BOOST_TEST_SUITE("Boost.Fiber: fiber test suite");
test->add( BOOST_TEST_CASE( & test_scheduler_dtor) );
test->add( BOOST_TEST_CASE( & test_join_fn) );
test->add( BOOST_TEST_CASE( & test_join_memfn) );
test->add( BOOST_TEST_CASE( & test_join_copyable) );
test->add( BOOST_TEST_CASE( & test_join_moveable) );
test->add( BOOST_TEST_CASE( & test_join_lambda) );
test->add( BOOST_TEST_CASE( & test_join_bind) );
test->add( BOOST_TEST_CASE( & test_join_in_fiber) );
test->add( BOOST_TEST_CASE( & test_move_fiber) );
test->add( BOOST_TEST_CASE( & test_yield) );
test->add( BOOST_TEST_CASE( & test_sleep_for) );
test->add( BOOST_TEST_CASE( & test_sleep_until) );
test->add( BOOST_TEST_CASE( & test_detach) );
return test;
}