441 lines
11 KiB
C++
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::dispatch, 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::dispatch, fn1);
|
|
f.join();
|
|
BOOST_CHECK_EQUAL( value1, 1);
|
|
}
|
|
{
|
|
value1 = 0;
|
|
value2 = "";
|
|
boost::fibers::fiber f( boost::fibers::launch::dispatch, 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::dispatch, & 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::dispatch, 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::dispatch, 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::dispatch, [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::dispatch, [](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::dispatch, 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::dispatch, 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::dispatch, 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::dispatch, 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::dispatch, 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::dispatch, 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::dispatch, 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::dispatch, fn3, std::ref( v1) );
|
|
boost::fibers::fiber f2( boost::fibers::launch::dispatch, 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::dispatch, (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::dispatch, (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;
|
|
}
|