// 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 #include #include #include #include #include #include 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; }