]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 <algorithm> | |
8 | #include <iostream> | |
9 | #include <sstream> | |
10 | #include <stdexcept> | |
11 | #include <string> | |
12 | #include <vector> | |
13 | ||
14 | #include <cstdio> | |
15 | ||
16 | #include <boost/assert.hpp> | |
17 | #include <boost/bind.hpp> | |
18 | #include <boost/foreach.hpp> | |
19 | #include <boost/lexical_cast.hpp> | |
20 | #include <boost/move/move.hpp> | |
21 | #include <boost/range.hpp> | |
22 | #include <boost/ref.hpp> | |
23 | #include <boost/test/unit_test.hpp> | |
24 | #include <boost/tuple/tuple.hpp> | |
25 | #include <boost/utility.hpp> | |
26 | ||
27 | #include <boost/coroutine/symmetric_coroutine.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 | ||
43 | X() : | |
44 | i( 0) | |
45 | {} | |
46 | ||
47 | X( int i_) : | |
48 | i( i_) | |
49 | {} | |
50 | }; | |
51 | ||
52 | X * p = 0; | |
53 | ||
54 | struct Y | |
55 | { | |
56 | Y() | |
57 | { value2 = 7; } | |
58 | ||
59 | ~Y() | |
60 | { value2 = 0; } | |
61 | }; | |
62 | ||
63 | template< typename X, typename I > | |
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 | { | |
76 | 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 | ||
88 | D( coro::symmetric_coroutine< void* >::yield_type & yield_) : | |
89 | B(), | |
90 | count( 0), | |
91 | call(), | |
92 | yield( & yield_) | |
93 | {} | |
94 | ||
95 | void foo() {} | |
96 | ||
97 | void resume() | |
98 | { call( 0); } | |
99 | ||
100 | void suspend() | |
101 | { ( *yield)(); } | |
102 | ||
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 | ||
117 | T() : | |
118 | d( 0) | |
119 | {} | |
120 | }; | |
121 | ||
122 | class copyable | |
123 | { | |
124 | public: | |
125 | bool state; | |
126 | ||
127 | copyable() : | |
128 | state( false) | |
129 | {} | |
130 | ||
131 | copyable( int) : | |
132 | state( true) | |
133 | {} | |
134 | ||
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 | ||
150 | moveable() : | |
151 | state( false) | |
152 | {} | |
153 | ||
154 | moveable( int) : | |
155 | state( true) | |
156 | {} | |
157 | ||
158 | moveable( BOOST_RV_REF( moveable) other) : | |
159 | state( false) | |
160 | { std::swap( state, other.state); } | |
161 | ||
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 | ||
170 | void operator()( coro::symmetric_coroutine< int >::yield_type &) | |
171 | { value1 = state; } | |
172 | }; | |
173 | ||
174 | void empty( coro::symmetric_coroutine< void >::yield_type &) {} | |
175 | ||
176 | void f2( coro::symmetric_coroutine< void >::yield_type &) | |
177 | { ++value2; } | |
178 | ||
179 | void f3( coro::symmetric_coroutine< X >::yield_type & yield) | |
180 | { value2 = yield.get().i; } | |
181 | ||
182 | void f4( coro::symmetric_coroutine< X& >::yield_type & yield) | |
183 | { | |
184 | X & x = yield.get(); | |
185 | p = & x; | |
186 | } | |
187 | ||
188 | void f5( coro::symmetric_coroutine< X* >::yield_type & yield) | |
189 | { p = yield.get(); } | |
190 | ||
191 | void f6( coro::symmetric_coroutine< void >::yield_type & yield) | |
192 | { | |
193 | Y y; | |
194 | yield( *term_coro); | |
195 | } | |
196 | ||
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 > | |
205 | void f9( coro::symmetric_coroutine< void >::yield_type &, E const& e) | |
206 | { throw e; } | |
207 | ||
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 | ||
216 | void f101( coro::symmetric_coroutine< int >::yield_type & yield) | |
217 | { value2 = yield.get(); } | |
218 | ||
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 | ||
226 | void f111( coro::symmetric_coroutine< void >::yield_type &) | |
227 | { value2 = 3; } | |
228 | ||
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 | ||
236 | void f121( coro::symmetric_coroutine< X& >::yield_type & yield) | |
237 | { p = & yield.get(); } | |
238 | ||
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 | ||
247 | void f141( coro::symmetric_coroutine< std::string >::yield_type & yield) | |
248 | { value3 = yield.get(); } | |
249 | ||
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 | ||
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 | ||
272 | void f16( coro::symmetric_coroutine< int >::yield_type & yield) | |
273 | { | |
274 | while ( yield) | |
275 | { | |
276 | value2 = yield.get(); | |
277 | yield(); | |
278 | } | |
279 | } | |
280 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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) ); | |
7c673cae FG |
600 | test->add( BOOST_TEST_CASE( & test_move_coro) ); |
601 | test->add( BOOST_TEST_CASE( & test_vptr) ); | |
602 | ||
603 | return test; | |
604 | } |