1
2 // Copyright Oliver Kowalke 2009.
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 #include <boost/coroutine/symmetric_coroutine.hpp>
8
9 #include <algorithm>
10 #include <iostream>
11 #include <sstream>
12 #include <stdexcept>
13 #include <string>
14 #include <vector>
15
16 #include <cstdio>
17
18 #include <boost/assert.hpp>
19 #include <boost/bind.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/lexical_cast.hpp>
22 #include <boost/move/move.hpp>
23 #include <boost/range.hpp>
24 #include <boost/ref.hpp>
25 #include <boost/test/unit_test.hpp>
26 #include <boost/tuple/tuple.hpp>
27 #include <boost/utility.hpp>
28
29 namespace coro = boost::coroutines;
30
31 bool value1 = false;
32 int value2 = 0;
33 std::string value3;
34
35 typedef void( * coro_fn_void)(coro::symmetric_coroutine< void* >::yield_type &);
36
37 coro::symmetric_coroutine< void >::call_type * term_coro = 0;
38
39 struct X
40 {
41 int i;
42
XX43 X() :
44 i( 0)
45 {}
46
XX47 X( int i_) :
48 i( i_)
49 {}
50 };
51
52 X * p = 0;
53
54 struct Y
55 {
YY56 Y()
57 { value2 = 7; }
58
~YY59 ~Y()
60 { value2 = 0; }
61 };
62
63 template< typename X, typename I >
trampoline(coro::symmetric_coroutine<void * >::yield_type & yield)64 void trampoline( coro::symmetric_coroutine< void* >::yield_type & yield)
65 {
66 void * vp = yield.get();
67 X * x = static_cast< X * >( vp);
68 I i( yield);
69 x->d = & i;
70 i.suspend();
71 i.run();
72 }
73
74 struct B
75 {
~BB76 virtual ~B() {}
77
78 virtual void foo() = 0;
79 };
80
81 class D : public B
82 {
83 public:
84 int count;
85 coro::symmetric_coroutine< void* >::call_type call;
86 coro::symmetric_coroutine< void* >::yield_type * yield;
87
D(coro::symmetric_coroutine<void * >::yield_type & yield_)88 D( coro::symmetric_coroutine< void* >::yield_type & yield_) :
89 B(),
90 count( 0),
91 call(),
92 yield( & yield_)
93 {}
94
foo()95 void foo() {}
96
resume()97 void resume()
98 { call( 0); }
99
suspend()100 void suspend()
101 { ( *yield)(); }
102
run()103 void run()
104 {
105 while ( yield && * yield)
106 {
107 ++count;
108 suspend();
109 }
110 }
111 };
112
113 struct T
114 {
115 D * d;
116
TT117 T() :
118 d( 0)
119 {}
120 };
121
122 class copyable
123 {
124 public:
125 bool state;
126
copyable()127 copyable() :
128 state( false)
129 {}
130
copyable(int)131 copyable( int) :
132 state( true)
133 {}
134
operator ()(coro::symmetric_coroutine<bool>::yield_type & yield)135 void operator()( coro::symmetric_coroutine< bool >::yield_type & yield)
136 {
137 if ( yield)
138 value1 = yield.get();
139 }
140 };
141
142 class moveable
143 {
144 private:
145 BOOST_MOVABLE_BUT_NOT_COPYABLE( moveable)
146
147 public:
148 bool state;
149
moveable()150 moveable() :
151 state( false)
152 {}
153
moveable(int)154 moveable( int) :
155 state( true)
156 {}
157
moveable(BOOST_RV_REF (moveable)other)158 moveable( BOOST_RV_REF( moveable) other) :
159 state( false)
160 { std::swap( state, other.state); }
161
operator =(BOOST_RV_REF (moveable)other)162 moveable & operator=( BOOST_RV_REF( moveable) other)
163 {
164 if ( this == & other) return * this;
165 moveable tmp( boost::move( other) );
166 std::swap( state, tmp.state);
167 return * this;
168 }
169
operator ()(coro::symmetric_coroutine<int>::yield_type &)170 void operator()( coro::symmetric_coroutine< int >::yield_type &)
171 { value1 = state; }
172 };
173
empty(coro::symmetric_coroutine<void>::yield_type &)174 void empty( coro::symmetric_coroutine< void >::yield_type &) {}
175
f2(coro::symmetric_coroutine<void>::yield_type &)176 void f2( coro::symmetric_coroutine< void >::yield_type &)
177 { ++value2; }
178
f3(coro::symmetric_coroutine<X>::yield_type & yield)179 void f3( coro::symmetric_coroutine< X >::yield_type & yield)
180 { value2 = yield.get().i; }
181
f4(coro::symmetric_coroutine<X &>::yield_type & yield)182 void f4( coro::symmetric_coroutine< X& >::yield_type & yield)
183 {
184 X & x = yield.get();
185 p = & x;
186 }
187
f5(coro::symmetric_coroutine<X * >::yield_type & yield)188 void f5( coro::symmetric_coroutine< X* >::yield_type & yield)
189 { p = yield.get(); }
190
f6(coro::symmetric_coroutine<void>::yield_type & yield)191 void f6( coro::symmetric_coroutine< void >::yield_type & yield)
192 {
193 Y y;
194 yield( *term_coro);
195 }
196
f7(coro::symmetric_coroutine<int>::yield_type & yield)197 void f7( coro::symmetric_coroutine< int >::yield_type & yield)
198 {
199 value2 = yield.get();
200 yield( *term_coro);
201 value2 = yield.get();
202 }
203
204 template< typename E >
f9(coro::symmetric_coroutine<void>::yield_type &,E const & e)205 void f9( coro::symmetric_coroutine< void >::yield_type &, E const& e)
206 { throw e; }
207
f10(coro::symmetric_coroutine<int>::yield_type & yield,coro::symmetric_coroutine<int>::call_type & other)208 void f10( coro::symmetric_coroutine< int >::yield_type & yield,
209 coro::symmetric_coroutine< int >::call_type & other)
210 {
211 int i = yield.get();
212 yield( other, i);
213 value2 = yield.get();
214 }
215
f101(coro::symmetric_coroutine<int>::yield_type & yield)216 void f101( coro::symmetric_coroutine< int >::yield_type & yield)
217 { value2 = yield.get(); }
218
f11(coro::symmetric_coroutine<void>::yield_type & yield,coro::symmetric_coroutine<void>::call_type & other)219 void f11( coro::symmetric_coroutine< void >::yield_type & yield,
220 coro::symmetric_coroutine< void >::call_type & other)
221 {
222 yield( other);
223 value2 = 7;
224 }
225
f111(coro::symmetric_coroutine<void>::yield_type &)226 void f111( coro::symmetric_coroutine< void >::yield_type &)
227 { value2 = 3; }
228
f12(coro::symmetric_coroutine<X &>::yield_type & yield,coro::symmetric_coroutine<X &>::call_type & other)229 void f12( coro::symmetric_coroutine< X& >::yield_type & yield,
230 coro::symmetric_coroutine< X& >::call_type & other)
231 {
232 yield( other, yield.get());
233 p = & yield.get();
234 }
235
f121(coro::symmetric_coroutine<X &>::yield_type & yield)236 void f121( coro::symmetric_coroutine< X& >::yield_type & yield)
237 { p = & yield.get(); }
238
f14(coro::symmetric_coroutine<int>::yield_type & yield,coro::symmetric_coroutine<std::string>::call_type & other)239 void f14( coro::symmetric_coroutine< int >::yield_type & yield,
240 coro::symmetric_coroutine< std::string >::call_type & other)
241 {
242 std::string str( boost::lexical_cast< std::string >( yield.get() ) );
243 yield( other, str);
244 value2 = yield.get();
245 }
246
f141(coro::symmetric_coroutine<std::string>::yield_type & yield)247 void f141( coro::symmetric_coroutine< std::string >::yield_type & yield)
248 { value3 = yield.get(); }
249
f15(coro::symmetric_coroutine<int>::yield_type & yield,int offset,coro::symmetric_coroutine<int>::call_type & other)250 void f15( coro::symmetric_coroutine< int >::yield_type & yield,
251 int offset,
252 coro::symmetric_coroutine< int >::call_type & other)
253 {
254 int x = yield.get();
255 value2 += x + offset;
256 yield( other, x);
257 x = yield.get();
258 value2 += x + offset;
259 yield( other, x);
260 }
261
f151(coro::symmetric_coroutine<int>::yield_type & yield,int offset)262 void f151( coro::symmetric_coroutine< int >::yield_type & yield,
263 int offset)
264 {
265 int x = yield.get();
266 value2 += x + offset;
267 yield();
268 x = yield.get();
269 value2 += x + offset;
270 }
271
f16(coro::symmetric_coroutine<int>::yield_type & yield)272 void f16( coro::symmetric_coroutine< int >::yield_type & yield)
273 {
274 while ( yield)
275 {
276 value2 = yield.get();
277 yield();
278 }
279 }
280
test_move()281 void test_move()
282 {
283 {
284 coro::symmetric_coroutine< void >::call_type coro1;
285 coro::symmetric_coroutine< void >::call_type coro2( empty);
286 BOOST_CHECK( ! coro1);
287 BOOST_CHECK( coro2);
288 coro1 = boost::move( coro2);
289 BOOST_CHECK( coro1);
290 BOOST_CHECK( ! coro2);
291 }
292
293 {
294 value1 = false;
295 copyable cp( 3);
296 BOOST_CHECK( cp.state);
297 BOOST_CHECK( ! value1);
298 coro::symmetric_coroutine< bool >::call_type coro( cp);
299 coro( true);
300 BOOST_CHECK( cp.state);
301 BOOST_CHECK( value1);
302 }
303
304 {
305 value1 = false;
306 moveable mv( 7);
307 BOOST_CHECK( mv.state);
308 BOOST_CHECK( ! value1);
309 coro::symmetric_coroutine< int >::call_type coro( boost::move( mv) );
310 coro( 7);
311 BOOST_CHECK( ! mv.state);
312 BOOST_CHECK( value1);
313 }
314 }
315
test_complete()316 void test_complete()
317 {
318 value2 = 0;
319
320 coro::symmetric_coroutine< void >::call_type coro( f2);
321 BOOST_CHECK( coro);
322 coro();
323 BOOST_CHECK( ! coro);
324 BOOST_CHECK_EQUAL( ( int)1, value2);
325 }
326
test_yield()327 void test_yield()
328 {
329 value2 = 0;
330
331 coro::symmetric_coroutine< int >::call_type coro3(
332 boost::bind( f151, _1, 3) );
333 BOOST_CHECK( coro3);
334 coro::symmetric_coroutine< int >::call_type coro2(
335 boost::bind( f15, _1, 2, boost::ref( coro3) ) );
336 BOOST_CHECK( coro2);
337 coro::symmetric_coroutine< int >::call_type coro1(
338 boost::bind( f15, _1, 1, boost::ref( coro2) ) );
339 BOOST_CHECK( coro1);
340
341 BOOST_CHECK_EQUAL( ( int)0, value2);
342 coro1( 1);
343 BOOST_CHECK( coro3);
344 BOOST_CHECK( coro2);
345 BOOST_CHECK( coro1);
346 BOOST_CHECK_EQUAL( ( int)9, value2);
347 coro1( 2);
348 BOOST_CHECK( ! coro3);
349 BOOST_CHECK( coro2);
350 BOOST_CHECK( coro1);
351 BOOST_CHECK_EQUAL( ( int)21, value2);
352 }
353
test_pass_value()354 void test_pass_value()
355 {
356 value2 = 0;
357
358 X x(7);
359 BOOST_CHECK_EQUAL( ( int)7, x.i);
360 BOOST_CHECK_EQUAL( 0, value2);
361 coro::symmetric_coroutine< X >::call_type coro( f3);
362 BOOST_CHECK( coro);
363 coro(7);
364 BOOST_CHECK( ! coro);
365 BOOST_CHECK_EQUAL( ( int)7, x.i);
366 BOOST_CHECK_EQUAL( 7, value2);
367 }
368
test_pass_reference()369 void test_pass_reference()
370 {
371 p = 0;
372
373 X x;
374 coro::symmetric_coroutine< X& >::call_type coro( f4);
375 BOOST_CHECK( coro);
376 coro( x);
377 BOOST_CHECK( ! coro);
378 BOOST_CHECK( p == & x);
379 }
380
test_pass_pointer()381 void test_pass_pointer()
382 {
383 p = 0;
384
385 X x;
386 coro::symmetric_coroutine< X* >::call_type coro( f5);
387 BOOST_CHECK( coro);
388 coro( & x);
389 BOOST_CHECK( ! coro);
390 BOOST_CHECK( p == & x);
391 }
392
test_unwind()393 void test_unwind()
394 {
395 value2 = 0;
396 {
397 coro::symmetric_coroutine< void >::call_type coro( f6);
398 coro::symmetric_coroutine< void >::call_type coro_e( empty);
399 BOOST_CHECK( coro);
400 BOOST_CHECK( coro_e);
401 term_coro = & coro_e;
402 BOOST_CHECK_EQUAL( ( int) 0, value2);
403 coro();
404 BOOST_CHECK( coro);
405 BOOST_CHECK_EQUAL( ( int) 7, value2);
406 }
407 BOOST_CHECK_EQUAL( ( int) 0, value2);
408 }
409
test_no_unwind()410 void test_no_unwind()
411 {
412 value2 = 0;
413 {
414 coro::symmetric_coroutine< void >::call_type coro( f6,
415 coro::attributes(
416 coro::stack_allocator::traits_type::default_size(),
417 coro::no_stack_unwind) );
418 coro::symmetric_coroutine< void >::call_type coro_e( empty);
419 BOOST_CHECK( coro);
420 BOOST_CHECK( coro_e);
421 term_coro = & coro_e;
422 BOOST_CHECK_EQUAL( ( int) 0, value2);
423 coro();
424 BOOST_CHECK( coro);
425 BOOST_CHECK_EQUAL( ( int) 7, value2);
426 }
427 BOOST_CHECK_EQUAL( ( int) 7, value2);
428 }
429
test_termination()430 void test_termination()
431 {
432 value2 = 0;
433
434 coro::symmetric_coroutine< int >::call_type coro( f7);
435 coro::symmetric_coroutine< void >::call_type coro_e( empty);
436 BOOST_CHECK( coro);
437 BOOST_CHECK( coro_e);
438 term_coro = & coro_e;
439 BOOST_CHECK_EQUAL( ( int) 0, value2);
440 coro(3);
441 BOOST_CHECK( coro);
442 BOOST_CHECK_EQUAL( ( int) 3, value2);
443 coro(7);
444 BOOST_CHECK( ! coro);
445 BOOST_CHECK_EQUAL( ( int) 7, value2);
446 }
447
test_yield_to_void()448 void test_yield_to_void()
449 {
450 value2 = 0;
451
452 coro::symmetric_coroutine< void >::call_type coro_other( f111);
453 coro::symmetric_coroutine< void >::call_type coro( boost::bind( f11, _1, boost::ref( coro_other) ) );
454 BOOST_CHECK( coro_other);
455 BOOST_CHECK( coro);
456 BOOST_CHECK_EQUAL( ( int) 0, value2);
457 coro();
458 BOOST_CHECK( ! coro_other);
459 BOOST_CHECK( coro);
460 BOOST_CHECK_EQUAL( ( int) 3, value2);
461 coro();
462 BOOST_CHECK( ! coro_other);
463 BOOST_CHECK( ! coro);
464 BOOST_CHECK_EQUAL( ( int) 7, value2);
465 }
466
test_yield_to_int()467 void test_yield_to_int()
468 {
469 value2 = 0;
470
471 coro::symmetric_coroutine< int >::call_type coro_other( f101);
472 coro::symmetric_coroutine< int >::call_type coro( boost::bind( f10, _1, boost::ref( coro_other) ) );
473 BOOST_CHECK( coro_other);
474 BOOST_CHECK( coro);
475 BOOST_CHECK_EQUAL( ( int) 0, value2);
476 coro(3);
477 BOOST_CHECK( ! coro_other);
478 BOOST_CHECK( coro);
479 BOOST_CHECK_EQUAL( ( int) 3, value2);
480 coro(7);
481 BOOST_CHECK( ! coro_other);
482 BOOST_CHECK( ! coro);
483 BOOST_CHECK_EQUAL( ( int) 7, value2);
484 }
485
test_yield_to_ref()486 void test_yield_to_ref()
487 {
488 p = 0;
489
490 coro::symmetric_coroutine< X& >::call_type coro_other( f121);
491 coro::symmetric_coroutine< X& >::call_type coro( boost::bind( f12, _1, boost::ref( coro_other) ) );
492 BOOST_CHECK( coro_other);
493 BOOST_CHECK( coro);
494 BOOST_CHECK( 0 == p);
495 X x1(3);
496 coro( x1);
497 BOOST_CHECK( ! coro_other);
498 BOOST_CHECK( coro);
499 BOOST_CHECK_EQUAL( p->i, x1.i);
500 BOOST_CHECK( p == & x1);
501 X x2(7);
502 coro(x2);
503 BOOST_CHECK( ! coro_other);
504 BOOST_CHECK( ! coro);
505 BOOST_CHECK_EQUAL( p->i, x2.i);
506 BOOST_CHECK( p == & x2);
507 }
508
test_yield_to_different()509 void test_yield_to_different()
510 {
511 value2 = 0;
512 value3 = "";
513
514 coro::symmetric_coroutine< std::string >::call_type coro_other( f141);
515 coro::symmetric_coroutine< int >::call_type coro( boost::bind( f14, _1, boost::ref( coro_other) ) );
516 BOOST_CHECK( coro_other);
517 BOOST_CHECK( coro);
518 BOOST_CHECK_EQUAL( ( int) 0, value2);
519 BOOST_CHECK( value3.empty() );
520 coro(3);
521 BOOST_CHECK( ! coro_other);
522 BOOST_CHECK( coro);
523 BOOST_CHECK_EQUAL( "3", value3);
524 coro(7);
525 BOOST_CHECK( ! coro_other);
526 BOOST_CHECK( ! coro);
527 BOOST_CHECK_EQUAL( ( int) 7, value2);
528 }
529
test_move_coro()530 void test_move_coro()
531 {
532 value2 = 0;
533
534 coro::symmetric_coroutine< int >::call_type coro1( f16);
535 coro::symmetric_coroutine< int >::call_type coro2;
536 BOOST_CHECK( coro1);
537 BOOST_CHECK( ! coro2);
538
539 coro1( 1);
540 BOOST_CHECK_EQUAL( ( int)1, value2);
541
542 coro2 = boost::move( coro1);
543 BOOST_CHECK( ! coro1);
544 BOOST_CHECK( coro2);
545
546 coro2( 2);
547 BOOST_CHECK_EQUAL( ( int)2, value2);
548
549 coro1 = boost::move( coro2);
550 BOOST_CHECK( coro1);
551 BOOST_CHECK( ! coro2);
552
553 coro1( 3);
554 BOOST_CHECK_EQUAL( ( int)3, value2);
555
556 coro2 = boost::move( coro1);
557 BOOST_CHECK( ! coro1);
558 BOOST_CHECK( coro2);
559
560 coro2( 4);
561 BOOST_CHECK_EQUAL( ( int)4, value2);
562 }
563
test_vptr()564 void test_vptr()
565 {
566 D * d = 0;
567 T t;
568 coro_fn_void fn = trampoline< T, D >;
569 coro::symmetric_coroutine< void* >::call_type call( fn);
570 call( & t);
571 d = t.d;
572 BOOST_CHECK( 0 != d);
573 d->call = boost::move( call);
574
575 BOOST_CHECK_EQUAL( ( int) 0, d->count);
576 d->resume();
577 BOOST_CHECK_EQUAL( ( int) 1, d->count);
578 d->resume();
579 BOOST_CHECK_EQUAL( ( int) 2, d->count);
580 }
581
init_unit_test_suite(int,char * [])582 boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
583 {
584 boost::unit_test::test_suite * test =
585 BOOST_TEST_SUITE("Boost.coroutine: symmetric coroutine test suite");
586
587 test->add( BOOST_TEST_CASE( & test_move) );
588 test->add( BOOST_TEST_CASE( & test_complete) );
589 test->add( BOOST_TEST_CASE( & test_yield) );
590 test->add( BOOST_TEST_CASE( & test_pass_value) );
591 test->add( BOOST_TEST_CASE( & test_pass_reference) );
592 test->add( BOOST_TEST_CASE( & test_pass_pointer) );
593 test->add( BOOST_TEST_CASE( & test_termination) );
594 test->add( BOOST_TEST_CASE( & test_unwind) );
595 test->add( BOOST_TEST_CASE( & test_no_unwind) );
596 test->add( BOOST_TEST_CASE( & test_yield_to_void) );
597 test->add( BOOST_TEST_CASE( & test_yield_to_int) );
598 test->add( BOOST_TEST_CASE( & test_yield_to_ref) );
599 test->add( BOOST_TEST_CASE( & test_yield_to_different) );
600 test->add( BOOST_TEST_CASE( & test_move_coro) );
601 test->add( BOOST_TEST_CASE( & test_vptr) );
602
603 return test;
604 }
605