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 <stdio.h>
8 #include <stdlib.h>
9
10 #include <cmath>
11 #include <cstdint>
12 #include <cstdio>
13 #include <iostream>
14 #include <memory>
15 #include <sstream>
16 #include <stdexcept>
17 #include <string>
18 #include <thread>
19 #include <utility>
20 #include <vector>
21
22 #include <boost/array.hpp>
23 #include <boost/assert.hpp>
24 #include <boost/lexical_cast.hpp>
25 #include <boost/test/unit_test.hpp>
26 #include <boost/utility.hpp>
27 #include <boost/variant.hpp>
28
29 #include <boost/context/fiber.hpp>
30 #include <boost/context/detail/config.hpp>
31
32 #ifdef BOOST_WINDOWS
33 #include <windows.h>
34 #endif
35
36 #if defined(BOOST_MSVC)
37 # pragma warning(push)
38 # pragma warning(disable: 4702 4723 4996)
39 #endif
40
41 typedef boost::variant<int,std::string> variant_t;
42
43 namespace ctx = boost::context;
44
45 int value1 = 0;
46 std::string value2;
47 double value3 = 0.;
48
49 struct X {
fooX50 ctx::fiber foo( ctx::fiber && f, int i) {
51 value1 = i;
52 return std::move( f);
53 }
54 };
55
56 struct Y {
YY57 Y() {
58 value1 = 3;
59 }
60
61 Y( Y const&) = delete;
62 Y & operator=( Y const&) = delete;
63
~YY64 ~Y() {
65 value1 = 7;
66 }
67 };
68
69 class moveable {
70 public:
71 bool state;
72 int value;
73
moveable()74 moveable() :
75 state( false),
76 value( -1) {
77 }
78
moveable(int v)79 moveable( int v) :
80 state( true),
81 value( v) {
82 }
83
moveable(moveable && other)84 moveable( moveable && other) :
85 state( other.state),
86 value( other.value) {
87 other.state = false;
88 other.value = -1;
89 }
90
operator =(moveable && other)91 moveable & operator=( moveable && other) {
92 if ( this == & other) return * this;
93 state = other.state;
94 value = other.value;
95 other.state = false;
96 other.value = -1;
97 return * this;
98 }
99
100 moveable( moveable const& other) = delete;
101 moveable & operator=( moveable const& other) = delete;
102
operator ()()103 void operator()() {
104 value1 = value;
105 }
106 };
107
108 struct my_exception : public std::runtime_error {
109 ctx::fiber f;
my_exceptionmy_exception110 my_exception( ctx::fiber && f_, char const* what) :
111 std::runtime_error( what),
112 f{ std::move( f_) } {
113 }
114 };
115
116 #ifdef BOOST_MSVC
117 // Optimizations can remove the integer-divide-by-zero here.
118 #pragma optimize("", off)
seh(bool & catched)119 void seh( bool & catched) {
120 __try {
121 int i = 1;
122 i /= 0;
123 } __except( EXCEPTION_EXECUTE_HANDLER) {
124 catched = true;
125 }
126 }
127 #pragma optimize("", on)
128 #endif
129
test_move()130 void test_move() {
131 value1 = 0;
132 int i = 1;
133 BOOST_CHECK_EQUAL( 0, value1);
134 ctx::fiber f1{
135 [&i](ctx::fiber && f) {
136 value1 = i;
137 f = std::move( f).resume();
138 value1 = i;
139 return std::move( f);
140 }};
141 f1 = std::move( f1).resume();
142 BOOST_CHECK_EQUAL( 1, value1);
143 BOOST_CHECK( f1);
144 ctx::fiber f2;
145 BOOST_CHECK( ! f2);
146 f2 = std::move( f1);
147 BOOST_CHECK( ! f1);
148 BOOST_CHECK( f2);
149 i = 3;
150 f2 = std::move( f2).resume();
151 BOOST_CHECK_EQUAL( 3, value1);
152 BOOST_CHECK( ! f1);
153 BOOST_CHECK( ! f2);
154 }
155
test_bind()156 void test_bind() {
157 value1 = 0;
158 X x;
159 ctx::fiber f{ std::bind( & X::foo, x, std::placeholders::_1, 7) };
160 f = std::move( f).resume();
161 BOOST_CHECK_EQUAL( 7, value1);
162 }
163
test_exception()164 void test_exception() {
165 {
166 const char * what = "hello world";
167 ctx::fiber f{
168 [&what](ctx::fiber && f) {
169 try {
170 throw std::runtime_error( what);
171 } catch ( std::runtime_error const& e) {
172 value2 = e.what();
173 }
174 return std::move( f);
175 }};
176 f = std::move( f).resume();
177 BOOST_CHECK_EQUAL( std::string( what), value2);
178 BOOST_CHECK( ! f);
179 }
180 #ifdef BOOST_MSVC
181 {
182 bool catched = false;
183 std::thread([&catched](){
184 ctx::fiber f{ [&catched](ctx::fiber && f){
185 seh( catched);
186 return std::move( f);
187 }};
188 BOOST_CHECK( f);
189 f = std::move( f).resume();
190 }).join();
191 BOOST_CHECK( catched);
192 }
193 #endif
194 }
195
test_fp()196 void test_fp() {
197 value3 = 0.;
198 double d = 7.13;
199 ctx::fiber f{
200 [&d]( ctx::fiber && f) {
201 d += 3.45;
202 value3 = d;
203 return std::move( f);
204 }};
205 f = std::move( f).resume();
206 BOOST_CHECK_EQUAL( 10.58, value3);
207 BOOST_CHECK( ! f);
208 }
209
test_stacked()210 void test_stacked() {
211 value1 = 0;
212 value3 = 0.;
213 ctx::fiber f{
214 [](ctx::fiber && f) {
215 ctx::fiber f1{
216 [](ctx::fiber && f) {
217 value1 = 3;
218 return std::move( f);
219 }};
220 f1 = std::move( f1).resume();
221 value3 = 3.14;
222 return std::move( f);
223 }};
224 f = std::move( f).resume();
225 BOOST_CHECK_EQUAL( 3, value1);
226 BOOST_CHECK_EQUAL( 3.14, value3);
227 BOOST_CHECK( ! f);
228 }
229
test_prealloc()230 void test_prealloc() {
231 value1 = 0;
232 ctx::default_stack alloc;
233 ctx::stack_context sctx( alloc.allocate() );
234 void * sp = static_cast< char * >( sctx.sp) - 10;
235 std::size_t size = sctx.size - 10;
236 int i = 7;
237 ctx::fiber f{
238 std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc,
239 [&i]( ctx::fiber && f) {
240 value1 = i;
241 return std::move( f);
242 }};
243 f = std::move( f).resume();
244 BOOST_CHECK_EQUAL( 7, value1);
245 BOOST_CHECK( ! f);
246 }
247
test_ontop()248 void test_ontop() {
249 {
250 int i = 3;
251 ctx::fiber f{ [&i](ctx::fiber && f) {
252 for (;;) {
253 i *= 10;
254 f = std::move( f).resume();
255 }
256 return std::move( f);
257 }};
258 f = std::move( f).resume();
259 // Pass fn by reference to see if the types are properly decayed.
260 auto fn = [&i](ctx::fiber && f){
261 i -= 10;
262 return std::move( f);
263 };
264 f = std::move( f).resume_with(fn);
265 BOOST_CHECK( f);
266 BOOST_CHECK_EQUAL( i, 200);
267 }
268 {
269 ctx::fiber f1;
270 ctx::fiber f{ [&f1](ctx::fiber && f) {
271 f = std::move( f).resume();
272 BOOST_CHECK( ! f);
273 return std::move( f1);
274 }};
275 f = std::move( f).resume();
276 f = std::move( f).resume_with(
277 [&f1](ctx::fiber && f){
278 f1 = std::move( f);
279 return std::move( f);
280 });
281 }
282 }
283
test_ontop_exception()284 void test_ontop_exception() {
285 value1 = 0;
286 value2 = "";
287 ctx::fiber f{ [](ctx::fiber && f){
288 for (;;) {
289 value1 = 3;
290 try {
291 f = std::move( f).resume();
292 } catch ( my_exception & ex) {
293 value2 = ex.what();
294 return std::move( ex.f);
295 }
296 }
297 return std::move( f);
298 }};
299 f = std::move( f).resume();
300 BOOST_CHECK_EQUAL( 3, value1);
301 const char * what = "hello world";
302 f = std::move( f).resume_with(
303 [what](ctx::fiber && f){
304 throw my_exception( std::move( f), what);
305 return std::move( f);
306 });
307 BOOST_CHECK_EQUAL( 3, value1);
308 BOOST_CHECK_EQUAL( std::string( what), value2);
309 }
310
test_termination1()311 void test_termination1() {
312 {
313 value1 = 0;
314 ctx::fiber f{
315 [](ctx::fiber && f){
316 Y y;
317 f = std::move( f).resume();
318 return std::move(f);
319 }};
320 f = std::move( f).resume();
321 BOOST_CHECK_EQUAL( 3, value1);
322 }
323 BOOST_CHECK_EQUAL( 7, value1);
324 {
325 value1 = 0;
326 BOOST_CHECK_EQUAL( 0, value1);
327 ctx::fiber f{
328 [](ctx::fiber && f) {
329 value1 = 3;
330 return std::move( f);
331 }};
332 f = std::move( f).resume();
333 BOOST_CHECK_EQUAL( 3, value1);
334 BOOST_CHECK( ! f);
335 }
336 {
337 value1 = 0;
338 BOOST_CHECK_EQUAL( 0, value1);
339 int i = 3;
340 ctx::fiber f{
341 [&i](ctx::fiber && f){
342 value1 = i;
343 f = std::move( f).resume();
344 value1 = i;
345 return std::move( f);
346 }};
347 f = std::move( f).resume();
348 BOOST_CHECK( f);
349 BOOST_CHECK_EQUAL( i, value1);
350 BOOST_CHECK( f);
351 i = 7;
352 f = std::move( f).resume();
353 BOOST_CHECK( ! f);
354 BOOST_CHECK_EQUAL( i, value1);
355 }
356 }
357
test_termination2()358 void test_termination2() {
359 {
360 value1 = 0;
361 value3 = 0.0;
362 ctx::fiber f{
363 [](ctx::fiber && f){
364 Y y;
365 value1 = 3;
366 value3 = 4.;
367 f = std::move( f).resume();
368 value1 = 7;
369 value3 = 8.;
370 f = std::move( f).resume();
371 return std::move( f);
372 }};
373 BOOST_CHECK_EQUAL( 0, value1);
374 BOOST_CHECK_EQUAL( 0., value3);
375 f = std::move( f).resume();
376 BOOST_CHECK_EQUAL( 3, value1);
377 BOOST_CHECK_EQUAL( 4., value3);
378 f = std::move( f).resume();
379 }
380 BOOST_CHECK_EQUAL( 7, value1);
381 BOOST_CHECK_EQUAL( 8., value3);
382 }
383
test_sscanf()384 void test_sscanf() {
385 ctx::fiber{
386 []( ctx::fiber && f) {
387 {
388 double n1 = 0;
389 double n2 = 0;
390 sscanf("3.14 7.13", "%lf %lf", & n1, & n2);
391 BOOST_CHECK( n1 == 3.14);
392 BOOST_CHECK( n2 == 7.13);
393 }
394 {
395 int n1=0;
396 int n2=0;
397 sscanf("1 23", "%d %d", & n1, & n2);
398 BOOST_CHECK( n1 == 1);
399 BOOST_CHECK( n2 == 23);
400 }
401 {
402 int n1=0;
403 int n2=0;
404 sscanf("1 jjj 23", "%d %*[j] %d", & n1, & n2);
405 BOOST_CHECK( n1 == 1);
406 BOOST_CHECK( n2 == 23);
407 }
408 return std::move( f);
409 }}.resume();
410 }
411
test_snprintf()412 void test_snprintf() {
413 ctx::fiber{
414 []( ctx::fiber && f) {
415 {
416 const char *fmt = "sqrt(2) = %f";
417 char buf[19];
418 snprintf( buf, sizeof( buf), fmt, std::sqrt( 2) );
419 BOOST_CHECK( 0 < sizeof( buf) );
420 BOOST_ASSERT( std::string("sqrt(2) = 1.41") == std::string( buf, 14) );
421 }
422 {
423 std::uint64_t n = 0xbcdef1234567890;
424 const char *fmt = "0x%016llX";
425 char buf[100];
426 snprintf( buf, sizeof( buf), fmt, n);
427 BOOST_ASSERT( std::string("0x0BCDEF1234567890") == std::string( buf, 18) );
428 }
429 return std::move( f);
430 }}.resume();
431 }
432
433 #ifdef BOOST_WINDOWS
test_bug12215()434 void test_bug12215() {
435 ctx::fiber{
436 [](ctx::fiber && f) {
437 char buffer[MAX_PATH];
438 GetModuleFileName( nullptr, buffer, MAX_PATH);
439 return std::move( f);
440 }}.resume();
441 }
442 #endif
443
test_goodcatch()444 void test_goodcatch() {
445 value1 = 0;
446 value3 = 0.0;
447 {
448 ctx::fiber f{
449 []( ctx::fiber && f) {
450 Y y;
451 value3 = 2.;
452 f = std::move( f).resume();
453 try {
454 value3 = 3.;
455 f = std::move( f).resume();
456 } catch ( boost::context::detail::forced_unwind const&) {
457 value3 = 4.;
458 throw;
459 } catch (...) {
460 value3 = 5.;
461 }
462 value3 = 6.;
463 return std::move( f);
464 }};
465 BOOST_CHECK_EQUAL( 0, value1);
466 BOOST_CHECK_EQUAL( 0., value3);
467 f = std::move( f).resume();
468 BOOST_CHECK_EQUAL( 3, value1);
469 BOOST_CHECK_EQUAL( 2., value3);
470 f = std::move( f).resume();
471 BOOST_CHECK_EQUAL( 3, value1);
472 BOOST_CHECK_EQUAL( 3., value3);
473 }
474 BOOST_CHECK_EQUAL( 7, value1);
475 BOOST_CHECK_EQUAL( 4., value3);
476 }
477
test_badcatch()478 void test_badcatch() {
479 #if 0
480 value1 = 0;
481 value3 = 0.;
482 {
483 ctx::fiber f{
484 []( ctx::fiber && f) {
485 Y y;
486 try {
487 value3 = 3.;
488 f = std::move( f).resume();
489 } catch (...) {
490 value3 = 5.;
491 }
492 return std::move( f);
493 }};
494 BOOST_CHECK_EQUAL( 0, value1);
495 BOOST_CHECK_EQUAL( 0., value3);
496 f = std::move( f).resume();
497 BOOST_CHECK_EQUAL( 3, value1);
498 BOOST_CHECK_EQUAL( 3., value3);
499 // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught
500 // in fn19. That will trigger the "not caught" assertion in ~forced_unwind. Getting that
501 // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good
502 // way to hook directly into the assertion when it happens on an alternate stack.
503 std::move( f);
504 }
505 BOOST_CHECK_EQUAL( 7, value1);
506 BOOST_CHECK_EQUAL( 4., value3);
507 #endif
508 }
509
init_unit_test_suite(int,char * [])510 boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
511 {
512 boost::unit_test::test_suite * test =
513 BOOST_TEST_SUITE("Boost.Context: fiber test suite");
514
515 test->add( BOOST_TEST_CASE( & test_move) );
516 test->add( BOOST_TEST_CASE( & test_bind) );
517 test->add( BOOST_TEST_CASE( & test_exception) );
518 test->add( BOOST_TEST_CASE( & test_fp) );
519 test->add( BOOST_TEST_CASE( & test_stacked) );
520 test->add( BOOST_TEST_CASE( & test_prealloc) );
521 test->add( BOOST_TEST_CASE( & test_ontop) );
522 test->add( BOOST_TEST_CASE( & test_ontop_exception) );
523 test->add( BOOST_TEST_CASE( & test_termination1) );
524 test->add( BOOST_TEST_CASE( & test_termination2) );
525 test->add( BOOST_TEST_CASE( & test_sscanf) );
526 test->add( BOOST_TEST_CASE( & test_snprintf) );
527 #ifdef BOOST_WINDOWS
528 test->add( BOOST_TEST_CASE( & test_bug12215) );
529 #endif
530 test->add( BOOST_TEST_CASE( & test_goodcatch) );
531 test->add( BOOST_TEST_CASE( & test_badcatch) );
532
533 return test;
534 }
535
536 #if defined(BOOST_MSVC)
537 # pragma warning(pop)
538 #endif
539