| 1 | |
| 2 | // Copyright Oliver Kowalke 2013. |
| 3 | // Distributed under the Boost Software License, Version 1.0. |
| 4 | // (See accompanying file LICENSE_1_0.txt or copy at |
| 5 | // http://www.boost.org/LICENSE_1_0.txt) |
| 6 | // |
| 7 | // This test is based on the tests of Boost.Thread |
| 8 | |
| 9 | #include <chrono> |
| 10 | #include <mutex> |
| 11 | #include <sstream> |
| 12 | #include <string> |
| 13 | |
| 14 | #include <boost/assert.hpp> |
| 15 | #include <boost/test/unit_test.hpp> |
| 16 | |
| 17 | #include <boost/fiber/all.hpp> |
| 18 | |
| 19 | int value1 = 0; |
| 20 | std::string value2 = "" ; |
| 21 | |
| 22 | struct X { |
| 23 | int value; |
| 24 | |
| 25 | void foo( int i) { |
| 26 | value = i; |
| 27 | } |
| 28 | }; |
| 29 | |
| 30 | class copyable { |
| 31 | public: |
| 32 | bool state; |
| 33 | int value; |
| 34 | |
| 35 | copyable() : |
| 36 | state( false), |
| 37 | value( -1) { |
| 38 | } |
| 39 | |
| 40 | copyable( int v) : |
| 41 | state( true), |
| 42 | value( v) { |
| 43 | } |
| 44 | |
| 45 | void operator()() { |
| 46 | value1 = value; |
| 47 | } |
| 48 | }; |
| 49 | |
| 50 | class moveable { |
| 51 | public: |
| 52 | bool state; |
| 53 | int value; |
| 54 | |
| 55 | moveable() : |
| 56 | state( false), |
| 57 | value( -1) { |
| 58 | } |
| 59 | |
| 60 | moveable( int v) : |
| 61 | state( true), |
| 62 | value( v) { |
| 63 | } |
| 64 | |
| 65 | moveable( moveable && other) : |
| 66 | state( other.state), |
| 67 | value( other.value) { |
| 68 | other.state = false; |
| 69 | other.value = -1; |
| 70 | } |
| 71 | |
| 72 | moveable & operator=( moveable && other) { |
| 73 | if ( this == & other) return * this; |
| 74 | state = other.state; |
| 75 | value = other.value; |
| 76 | other.state = false; |
| 77 | other.value = -1; |
| 78 | return * this; |
| 79 | } |
| 80 | |
| 81 | moveable( moveable const& other) = delete; |
| 82 | moveable & operator=( moveable const& other) = delete; |
| 83 | |
| 84 | void operator()() { |
| 85 | value1 = value; |
| 86 | } |
| 87 | }; |
| 88 | |
| 89 | class detachable { |
| 90 | private: |
| 91 | int alive_count_; |
| 92 | |
| 93 | public: |
| 94 | static int alive_count; |
| 95 | static bool was_running; |
| 96 | |
| 97 | detachable() : |
| 98 | alive_count_( 1) { |
| 99 | ++alive_count; |
| 100 | } |
| 101 | |
| 102 | detachable( detachable const& g) : |
| 103 | alive_count_( g.alive_count_) { |
| 104 | ++alive_count; |
| 105 | } |
| 106 | |
| 107 | ~detachable() { |
| 108 | alive_count_ = 0; |
| 109 | --alive_count; |
| 110 | } |
| 111 | |
| 112 | void operator()() { |
| 113 | BOOST_CHECK_EQUAL(1, alive_count_); |
| 114 | was_running = true; |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | int detachable::alive_count = 0; |
| 119 | bool detachable::was_running = false; |
| 120 | |
| 121 | void fn1() { |
| 122 | value1 = 1; |
| 123 | } |
| 124 | |
| 125 | void fn2( int i, std::string const& s) { |
| 126 | value1 = i; |
| 127 | value2 = s; |
| 128 | } |
| 129 | |
| 130 | void fn3( int & i) { |
| 131 | i = 1; |
| 132 | boost::this_fiber::yield(); |
| 133 | i = 1; |
| 134 | boost::this_fiber::yield(); |
| 135 | i = 2; |
| 136 | boost::this_fiber::yield(); |
| 137 | i = 3; |
| 138 | boost::this_fiber::yield(); |
| 139 | i = 5; |
| 140 | boost::this_fiber::yield(); |
| 141 | i = 8; |
| 142 | } |
| 143 | |
| 144 | void fn4() { |
| 145 | boost::this_fiber::yield(); |
| 146 | } |
| 147 | |
| 148 | void fn5() { |
| 149 | boost::fibers::fiber f( boost::fibers::launch::post, fn4); |
| 150 | BOOST_CHECK( f.joinable() ); |
| 151 | f.join(); |
| 152 | BOOST_CHECK( ! f.joinable() ); |
| 153 | } |
| 154 | |
| 155 | void test_scheduler_dtor() { |
| 156 | boost::fibers::context * ctx( |
| 157 | boost::fibers::context::active() ); |
| 158 | (void)ctx; |
| 159 | } |
| 160 | |
| 161 | void test_join_fn() { |
| 162 | { |
| 163 | value1 = 0; |
| 164 | boost::fibers::fiber f( boost::fibers::launch::post, fn1); |
| 165 | f.join(); |
| 166 | BOOST_CHECK_EQUAL( value1, 1); |
| 167 | } |
| 168 | { |
| 169 | value1 = 0; |
| 170 | value2 = "" ; |
| 171 | boost::fibers::fiber f( boost::fibers::launch::post, fn2, 3, "abc" ); |
| 172 | f.join(); |
| 173 | BOOST_CHECK_EQUAL( value1, 3); |
| 174 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | void test_join_memfn() { |
| 179 | X x = {.value: 0}; |
| 180 | BOOST_CHECK_EQUAL( x.value, 0); |
| 181 | boost::fibers::fiber( boost::fibers::launch::post, & X::foo, & x, 3).join(); |
| 182 | BOOST_CHECK_EQUAL( x.value, 3); |
| 183 | } |
| 184 | |
| 185 | void test_join_copyable() { |
| 186 | value1 = 0; |
| 187 | copyable cp( 3); |
| 188 | BOOST_CHECK( cp.state); |
| 189 | BOOST_CHECK_EQUAL( value1, 0); |
| 190 | boost::fibers::fiber f( boost::fibers::launch::post, cp); |
| 191 | f.join(); |
| 192 | BOOST_CHECK( cp.state); |
| 193 | BOOST_CHECK_EQUAL( value1, 3); |
| 194 | } |
| 195 | |
| 196 | void test_join_moveable() { |
| 197 | value1 = 0; |
| 198 | moveable mv( 7); |
| 199 | BOOST_CHECK( mv.state); |
| 200 | BOOST_CHECK_EQUAL( value1, 0); |
| 201 | boost::fibers::fiber f( boost::fibers::launch::post, std::move( mv) ); |
| 202 | f.join(); |
| 203 | BOOST_CHECK( ! mv.state); |
| 204 | BOOST_CHECK_EQUAL( value1, 7); |
| 205 | } |
| 206 | |
| 207 | void test_join_lambda() { |
| 208 | { |
| 209 | value1 = 0; |
| 210 | value2 = "" ; |
| 211 | int i = 3; |
| 212 | std::string abc("abc" ); |
| 213 | boost::fibers::fiber f( |
| 214 | boost::fibers::launch::post, [i,abc]() { |
| 215 | value1 = i; |
| 216 | value2 = abc; |
| 217 | }); |
| 218 | f.join(); |
| 219 | BOOST_CHECK_EQUAL( value1, 3); |
| 220 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 221 | } |
| 222 | { |
| 223 | value1 = 0; |
| 224 | value2 = "" ; |
| 225 | int i = 3; |
| 226 | std::string abc("abc" ); |
| 227 | boost::fibers::fiber f( |
| 228 | boost::fibers::launch::post, [](int i, std::string const& abc) { |
| 229 | value1 = i; |
| 230 | value2 = abc; |
| 231 | }, |
| 232 | i, abc); |
| 233 | f.join(); |
| 234 | BOOST_CHECK_EQUAL( value1, 3); |
| 235 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | void test_join_bind() { |
| 240 | { |
| 241 | value1 = 0; |
| 242 | value2 = "" ; |
| 243 | int i = 3; |
| 244 | std::string abc("abc" ); |
| 245 | boost::fibers::fiber f( |
| 246 | boost::fibers::launch::post, std::bind( |
| 247 | f: [i,abc]() { |
| 248 | value1 = i; |
| 249 | value2 = abc; |
| 250 | } |
| 251 | )); |
| 252 | f.join(); |
| 253 | BOOST_CHECK_EQUAL( value1, 3); |
| 254 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 255 | } |
| 256 | { |
| 257 | value1 = 0; |
| 258 | value2 = "" ; |
| 259 | std::string abc("abc" ); |
| 260 | boost::fibers::fiber f( |
| 261 | boost::fibers::launch::post, std::bind( |
| 262 | f: [](std::string & str) { |
| 263 | value1 = 3; |
| 264 | value2 = str; |
| 265 | }, |
| 266 | args&: abc |
| 267 | )); |
| 268 | f.join(); |
| 269 | BOOST_CHECK_EQUAL( value1, 3); |
| 270 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 271 | } |
| 272 | { |
| 273 | value1 = 0; |
| 274 | value2 = "" ; |
| 275 | std::string abc("abc" ); |
| 276 | boost::fibers::fiber f( |
| 277 | boost::fibers::launch::post, std::bind( |
| 278 | f: []( std::string & str) { |
| 279 | value1 = 3; |
| 280 | value2 = str; |
| 281 | }, |
| 282 | args: std::placeholders::_1 |
| 283 | ), |
| 284 | std::ref( t&: abc) ); |
| 285 | f.join(); |
| 286 | BOOST_CHECK_EQUAL( value1, 3); |
| 287 | BOOST_CHECK_EQUAL( value2, "abc" ); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | void test_join_in_fiber() { |
| 292 | // spawn fiber f |
| 293 | // f spawns an new fiber f' in its fiber-fn |
| 294 | // f' yields in its fiber-fn |
| 295 | // f joins s' and gets suspended (waiting on s') |
| 296 | boost::fibers::fiber f( boost::fibers::launch::post, fn5); |
| 297 | BOOST_CHECK( f.joinable() ); |
| 298 | // join() resumes f + f' which completes |
| 299 | f.join(); |
| 300 | BOOST_CHECK( ! f.joinable() ); |
| 301 | } |
| 302 | |
| 303 | void test_move_fiber() { |
| 304 | boost::fibers::fiber f1; |
| 305 | BOOST_CHECK( ! f1.joinable() ); |
| 306 | boost::fibers::fiber f2( boost::fibers::launch::post, fn1); |
| 307 | BOOST_CHECK( f2.joinable() ); |
| 308 | f1 = std::move( f2); |
| 309 | BOOST_CHECK( f1.joinable() ); |
| 310 | BOOST_CHECK( ! f2.joinable() ); |
| 311 | f1.join(); |
| 312 | BOOST_CHECK( ! f1.joinable() ); |
| 313 | BOOST_CHECK( ! f2.joinable() ); |
| 314 | } |
| 315 | |
| 316 | void test_id() { |
| 317 | boost::fibers::fiber f1; |
| 318 | boost::fibers::fiber f2( boost::fibers::launch::post, fn1); |
| 319 | BOOST_CHECK( ! f1.joinable() ); |
| 320 | BOOST_CHECK( f2.joinable() ); |
| 321 | |
| 322 | BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f1.get_id() ); |
| 323 | BOOST_CHECK( boost::fibers::fiber::id() != f2.get_id() ); |
| 324 | |
| 325 | boost::fibers::fiber f3( boost::fibers::launch::post, fn1); |
| 326 | BOOST_CHECK( f2.get_id() != f3.get_id() ); |
| 327 | |
| 328 | f1 = std::move( f2); |
| 329 | BOOST_CHECK( f1.joinable() ); |
| 330 | BOOST_CHECK( ! f2.joinable() ); |
| 331 | |
| 332 | BOOST_CHECK( boost::fibers::fiber::id() != f1.get_id() ); |
| 333 | BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f2.get_id() ); |
| 334 | |
| 335 | BOOST_CHECK( ! f2.joinable() ); |
| 336 | |
| 337 | f1.join(); |
| 338 | f3.join(); |
| 339 | } |
| 340 | |
| 341 | void test_yield() { |
| 342 | int v1 = 0, v2 = 0; |
| 343 | BOOST_CHECK_EQUAL( 0, v1); |
| 344 | BOOST_CHECK_EQUAL( 0, v2); |
| 345 | boost::fibers::fiber f1( boost::fibers::launch::post, fn3, std::ref( t&: v1) ); |
| 346 | boost::fibers::fiber f2( boost::fibers::launch::post, fn3, std::ref( t&: v2) ); |
| 347 | f1.join(); |
| 348 | f2.join(); |
| 349 | BOOST_CHECK( ! f1.joinable() ); |
| 350 | BOOST_CHECK( ! f2.joinable() ); |
| 351 | BOOST_CHECK_EQUAL( 8, v1); |
| 352 | BOOST_CHECK_EQUAL( 8, v2); |
| 353 | } |
| 354 | |
| 355 | void test_sleep_for() { |
| 356 | typedef std::chrono::system_clock Clock; |
| 357 | typedef Clock::time_point time_point; |
| 358 | std::chrono::milliseconds ms(500); |
| 359 | time_point t0 = Clock::now(); |
| 360 | boost::this_fiber::sleep_for(timeout_duration: ms); |
| 361 | time_point t1 = Clock::now(); |
| 362 | std::chrono::nanoseconds ns = (t1 - t0) - ms; |
| 363 | std::chrono::nanoseconds err = ms / 10; |
| 364 | // This test is spurious as it depends on the time the fiber system switches the fiber |
| 365 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); |
| 366 | } |
| 367 | |
| 368 | void test_sleep_until() { |
| 369 | { |
| 370 | typedef std::chrono::steady_clock Clock; |
| 371 | typedef Clock::time_point time_point; |
| 372 | std::chrono::milliseconds ms(500); |
| 373 | time_point t0 = Clock::now(); |
| 374 | boost::this_fiber::sleep_until(sleep_time_: t0 + ms); |
| 375 | time_point t1 = Clock::now(); |
| 376 | std::chrono::nanoseconds ns = (t1 - t0) - ms; |
| 377 | std::chrono::nanoseconds err = ms / 10; |
| 378 | // This test is spurious as it depends on the time the thread system switches the threads |
| 379 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); |
| 380 | } |
| 381 | { |
| 382 | typedef std::chrono::system_clock Clock; |
| 383 | typedef Clock::time_point time_point; |
| 384 | std::chrono::milliseconds ms(500); |
| 385 | time_point t0 = Clock::now(); |
| 386 | boost::this_fiber::sleep_until(sleep_time_: t0 + ms); |
| 387 | time_point t1 = Clock::now(); |
| 388 | std::chrono::nanoseconds ns = (t1 - t0) - ms; |
| 389 | std::chrono::nanoseconds err = ms / 10; |
| 390 | // This test is spurious as it depends on the time the thread system switches the threads |
| 391 | BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | void do_wait( boost::fibers::barrier* b) { |
| 396 | b->wait(); |
| 397 | } |
| 398 | |
| 399 | void test_detach() { |
| 400 | { |
| 401 | boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); |
| 402 | BOOST_CHECK( f.joinable() ); |
| 403 | f.detach(); |
| 404 | BOOST_CHECK( ! f.joinable() ); |
| 405 | boost::this_fiber::sleep_for( timeout_duration: std::chrono::milliseconds(250) ); |
| 406 | BOOST_CHECK( detachable::was_running); |
| 407 | BOOST_CHECK_EQUAL( 0, detachable::alive_count); |
| 408 | } |
| 409 | { |
| 410 | boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); |
| 411 | BOOST_CHECK( f.joinable() ); |
| 412 | boost::this_fiber::yield(); |
| 413 | f.detach(); |
| 414 | BOOST_CHECK( ! f.joinable() ); |
| 415 | boost::this_fiber::sleep_for( timeout_duration: std::chrono::milliseconds(250) ); |
| 416 | BOOST_CHECK( detachable::was_running); |
| 417 | BOOST_CHECK_EQUAL( 0, detachable::alive_count); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { |
| 422 | boost::unit_test::test_suite * test = |
| 423 | BOOST_TEST_SUITE("Boost.Fiber: fiber test suite" ); |
| 424 | |
| 425 | test->add( BOOST_TEST_CASE( & test_scheduler_dtor) ); |
| 426 | test->add( BOOST_TEST_CASE( & test_join_fn) ); |
| 427 | test->add( BOOST_TEST_CASE( & test_join_memfn) ); |
| 428 | test->add( BOOST_TEST_CASE( & test_join_copyable) ); |
| 429 | test->add( BOOST_TEST_CASE( & test_join_moveable) ); |
| 430 | test->add( BOOST_TEST_CASE( & test_join_lambda) ); |
| 431 | test->add( BOOST_TEST_CASE( & test_join_bind) ); |
| 432 | test->add( BOOST_TEST_CASE( & test_join_in_fiber) ); |
| 433 | test->add( BOOST_TEST_CASE( & test_move_fiber) ); |
| 434 | test->add( BOOST_TEST_CASE( & test_yield) ); |
| 435 | test->add( BOOST_TEST_CASE( & test_sleep_for) ); |
| 436 | test->add( BOOST_TEST_CASE( & test_sleep_until) ); |
| 437 | test->add( BOOST_TEST_CASE( & test_detach) ); |
| 438 | |
| 439 | return test; |
| 440 | } |
| 441 | |