• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/continuation.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::continuation foo( ctx::continuation && c, int i) {
51         value1 = i;
52         return std::move( c);
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::continuation   c;
my_exceptionmy_exception110     my_exception( ctx::continuation && c_, char const* what) :
111         std::runtime_error( what),
112         c{ std::move( c_) } {
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::continuation c1 = ctx::callcc(
135         [&i](ctx::continuation && c) {
136             value1 = i;
137             c = c.resume();
138             value1 = i;
139             return std::move( c);
140         });
141     BOOST_CHECK_EQUAL( 1, value1);
142     BOOST_CHECK( c1);
143     ctx::continuation c2;
144     BOOST_CHECK( ! c2);
145     c2 = std::move( c1);
146     BOOST_CHECK( ! c1);
147     BOOST_CHECK( c2);
148     i = 3;
149     c2.resume();
150     BOOST_CHECK_EQUAL( 3, value1);
151     BOOST_CHECK( ! c1);
152     BOOST_CHECK( ! c2);
153 }
154 
test_bind()155 void test_bind() {
156     value1 = 0;
157     X x;
158     ctx::continuation c = ctx::callcc( std::bind( & X::foo, x, std::placeholders::_1, 7) );
159     BOOST_CHECK_EQUAL( 7, value1);
160 }
161 
test_exception()162 void test_exception() {
163     {
164         const char * what = "hello world";
165         ctx::continuation c = ctx::callcc(
166             [&what](ctx::continuation && c) {
167                 try {
168                     throw std::runtime_error( what);
169                 } catch ( std::runtime_error const& e) {
170                     value2 = e.what();
171                 }
172                 return std::move( c);
173             });
174         BOOST_CHECK_EQUAL( std::string( what), value2);
175         BOOST_CHECK( ! c);
176     }
177 #ifdef BOOST_MSVC
178     {
179         bool catched = false;
180         std::thread([&catched](){
181                 ctx::continuation c = ctx::callcc([&catched](ctx::continuation && c){
182                             c = c.resume();
183                             seh( catched);
184                             return std::move( c);
185                         });
186             BOOST_CHECK( c );
187             c.resume();
188         }).join();
189         BOOST_CHECK( catched);
190     }
191 #endif
192 }
193 
test_fp()194 void test_fp() {
195     value3 = 0.;
196     double d = 7.13;
197     ctx::continuation c = ctx::callcc(
198         [&d]( ctx::continuation && c) {
199             d += 3.45;
200             value3 = d;
201             return std::move( c);
202         });
203     BOOST_CHECK_EQUAL( 10.58, value3);
204     BOOST_CHECK( ! c);
205 }
206 
test_stacked()207 void test_stacked() {
208     value1 = 0;
209     value3 = 0.;
210     ctx::continuation c = ctx::callcc(
211         [](ctx::continuation && c) {
212             ctx::continuation c1 = ctx::callcc(
213                 [](ctx::continuation && c) {
214                     value1 = 3;
215                     return std::move( c);
216                 });
217             value3 = 3.14;
218             return std::move( c);
219         });
220     BOOST_CHECK_EQUAL( 3, value1);
221     BOOST_CHECK_EQUAL( 3.14, value3);
222     BOOST_CHECK( ! c );
223 }
224 
test_prealloc()225 void test_prealloc() {
226     value1 = 0;
227     ctx::default_stack alloc;
228     ctx::stack_context sctx( alloc.allocate() );
229     void * sp = static_cast< char * >( sctx.sp) - 10;
230     std::size_t size = sctx.size - 10;
231     int i = 7;
232     ctx::continuation c = ctx::callcc(
233         std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc,
234         [&i]( ctx::continuation && c) {
235             value1 = i;
236             return std::move( c);
237         });
238     BOOST_CHECK_EQUAL( 7, value1);
239     BOOST_CHECK( ! c);
240 }
241 
test_ontop()242 void test_ontop() {
243     {
244         int i = 3;
245         ctx::continuation c = ctx::callcc([&i](ctx::continuation && c) {
246                     for (;;) {
247                         i *= 10;
248                         c = c.resume();
249                     }
250                     return std::move( c);
251                 });
252         c = c.resume_with(
253                [&i](ctx::continuation && c){
254                    i -= 10;
255                    return std::move( c);
256                });
257         BOOST_CHECK( c);
258         BOOST_CHECK_EQUAL( i, 200);
259     }
260     {
261         ctx::continuation c1;
262         ctx::continuation c = ctx::callcc([&c1](ctx::continuation && c) {
263                     c = c.resume();
264                     BOOST_CHECK( ! c);
265                     return std::move( c1);
266                 });
267         c = c.resume_with(
268                [&c1](ctx::continuation && c){
269                    c1 = std::move( c);
270                    return std::move( c);
271                });
272     }
273 }
274 
test_ontop_exception()275 void test_ontop_exception() {
276     value1 = 0;
277     value2 = "";
278     ctx::continuation c = ctx::callcc([](ctx::continuation && c){
279             for (;;) {
280                 value1 = 3;
281                 try {
282                     c = c.resume();
283                 } catch ( my_exception & ex) {
284                     value2 = ex.what();
285                     return std::move( ex.c);
286                 }
287             }
288             return std::move( c);
289     });
290     c = c.resume();
291     BOOST_CHECK_EQUAL( 3, value1);
292     const char * what = "hello world";
293     c.resume_with(
294        [what](ctx::continuation && c){
295             throw my_exception( std::move( c), what);
296             return std::move( c);
297        });
298     BOOST_CHECK_EQUAL( 3, value1);
299     BOOST_CHECK_EQUAL( std::string( what), value2);
300 }
301 
test_termination1()302 void test_termination1() {
303     {
304         value1 = 0;
305         ctx::continuation c = ctx::callcc(
306             [](ctx::continuation && c){
307                 Y y;
308                 return c.resume();
309             });
310         BOOST_CHECK_EQUAL( 3, value1);
311     }
312     BOOST_CHECK_EQUAL( 7, value1);
313     {
314         value1 = 0;
315         BOOST_CHECK_EQUAL( 0, value1);
316         ctx::continuation c = ctx::callcc(
317             [](ctx::continuation && c) {
318                 value1 = 3;
319                 return std::move( c);
320             });
321         BOOST_CHECK_EQUAL( 3, value1);
322         BOOST_CHECK( ! c );
323     }
324     {
325         value1 = 0;
326         BOOST_CHECK_EQUAL( 0, value1);
327         int i = 3;
328         ctx::continuation c = ctx::callcc(
329             [&i](ctx::continuation && c){
330                 value1 = i;
331                 c = c.resume();
332                 value1 = i;
333                 return std::move( c);
334             });
335         BOOST_CHECK( c);
336         BOOST_CHECK_EQUAL( i, value1);
337         BOOST_CHECK( c);
338         i = 7;
339         c = c.resume();
340         BOOST_CHECK( ! c);
341         BOOST_CHECK_EQUAL( i, value1);
342     }
343 }
344 
test_termination2()345 void test_termination2() {
346     {
347         value1 = 0;
348         value3 = 0.0;
349         ctx::continuation c = ctx::callcc(
350             [](ctx::continuation && c){
351                 Y y;
352                 value1 = 3;
353                 value3 = 4.;
354                 c = c.resume();
355                 value1 = 7;
356                 value3 = 8.;
357                 c = c.resume();
358                 return std::move( c);
359             });
360         BOOST_CHECK_EQUAL( 3, value1);
361         BOOST_CHECK_EQUAL( 4., value3);
362         c = c.resume();
363     }
364     BOOST_CHECK_EQUAL( 7, value1);
365     BOOST_CHECK_EQUAL( 8., value3);
366 }
367 
test_sscanf()368 void test_sscanf() {
369     ctx::continuation c = ctx::callcc(
370 		[]( ctx::continuation && c) {
371 			{
372 				double n1 = 0;
373 				double n2 = 0;
374 				sscanf("3.14 7.13", "%lf %lf", & n1, & n2);
375 				BOOST_CHECK( n1 == 3.14);
376 				BOOST_CHECK( n2 == 7.13);
377 			}
378 			{
379 				int n1=0;
380 				int n2=0;
381 				sscanf("1 23", "%d %d", & n1, & n2);
382 				BOOST_CHECK( n1 == 1);
383 				BOOST_CHECK( n2 == 23);
384 			}
385 			{
386 				int n1=0;
387 				int n2=0;
388 				sscanf("1 jjj 23", "%d %*[j] %d", & n1, & n2);
389 				BOOST_CHECK( n1 == 1);
390 				BOOST_CHECK( n2 == 23);
391 			}
392 			return std::move( c);
393 	});
394 }
395 
test_snprintf()396 void test_snprintf() {
397     ctx::continuation c = ctx::callcc(
398 		[]( ctx::continuation && c) {
399             {
400                 const char *fmt = "sqrt(2) = %f";
401                 char buf[19];
402                 snprintf( buf, sizeof( buf), fmt, std::sqrt( 2) );
403                 BOOST_CHECK( 0 < sizeof( buf) );
404                 BOOST_ASSERT( std::string("sqrt(2) = 1.41") == std::string( buf, 14) );
405             }
406             {
407                 std::uint64_t n = 0xbcdef1234567890;
408                 const char *fmt = "0x%016llX";
409                 char buf[100];
410                 snprintf( buf, sizeof( buf), fmt, n);
411                 BOOST_ASSERT( std::string("0x0BCDEF1234567890") == std::string( buf, 18) );
412             }
413 			return std::move( c);
414 	});
415 }
416 
417 #ifdef BOOST_WINDOWS
test_bug12215()418 void test_bug12215() {
419         ctx::continuation c = ctx::callcc(
420             [](ctx::continuation && c) {
421                 char buffer[MAX_PATH];
422                 GetModuleFileName( nullptr, buffer, MAX_PATH);
423                 return std::move( c);
424             });
425 }
426 #endif
427 
test_goodcatch()428 void test_goodcatch() {
429     value1 = 0;
430     value3 = 0.0;
431     {
432         ctx::continuation c = ctx::callcc(
433             [](ctx::continuation && c) {
434                 Y y;
435                 value3 = 2.;
436                 c = c.resume();
437                 try {
438                     value3 = 3.;
439                     c = c.resume();
440                 } catch ( boost::context::detail::forced_unwind const&) {
441                     value3 = 4.;
442                     throw;
443                 } catch (...) {
444                     value3 = 5.;
445                 }
446                 value3 = 6.;
447                 return std::move( c);
448             });
449         BOOST_CHECK_EQUAL( 3, value1);
450         BOOST_CHECK_EQUAL( 2., value3);
451         c = c.resume();
452         BOOST_CHECK_EQUAL( 3, value1);
453         BOOST_CHECK_EQUAL( 3., value3);
454     }
455     BOOST_CHECK_EQUAL( 7, value1);
456     BOOST_CHECK_EQUAL( 4., value3);
457 }
458 
test_badcatch()459 void test_badcatch() {
460 #if 0
461     value1 = 0;
462     value3 = 0.;
463     {
464         ctx::continuation c = ctx::callcc(
465             [](ctx::continuation && c) {
466                 Y y;
467                 try {
468                     value3 = 3.;
469                     c = c.resume();
470                 } catch (...) {
471                     value3 = 5.;
472                 }
473                 return std::move( c);
474             });
475         BOOST_CHECK_EQUAL( 3, value1);
476         BOOST_CHECK_EQUAL( 3., value3);
477         // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught
478         // in fn19.  That will trigger the "not caught" assertion in ~forced_unwind.  Getting that
479         // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good
480         // way to hook directly into the assertion when it happens on an alternate stack.
481         std::move( c);
482     }
483     BOOST_CHECK_EQUAL( 7, value1);
484     BOOST_CHECK_EQUAL( 4., value3);
485 #endif
486 }
487 
init_unit_test_suite(int,char * [])488 boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
489 {
490     boost::unit_test::test_suite * test =
491         BOOST_TEST_SUITE("Boost.Context: callcc test suite");
492 
493     test->add( BOOST_TEST_CASE( & test_move) );
494     test->add( BOOST_TEST_CASE( & test_bind) );
495     test->add( BOOST_TEST_CASE( & test_exception) );
496     test->add( BOOST_TEST_CASE( & test_fp) );
497     test->add( BOOST_TEST_CASE( & test_stacked) );
498     test->add( BOOST_TEST_CASE( & test_prealloc) );
499     test->add( BOOST_TEST_CASE( & test_ontop) );
500     test->add( BOOST_TEST_CASE( & test_ontop_exception) );
501     test->add( BOOST_TEST_CASE( & test_termination1) );
502     test->add( BOOST_TEST_CASE( & test_termination2) );
503     test->add( BOOST_TEST_CASE( & test_sscanf) );
504     test->add( BOOST_TEST_CASE( & test_snprintf) );
505 #ifdef BOOST_WINDOWS
506     test->add( BOOST_TEST_CASE( & test_bug12215) );
507 #endif
508     test->add( BOOST_TEST_CASE( & test_goodcatch) );
509     test->add( BOOST_TEST_CASE( & test_badcatch) );
510 
511     return test;
512 }
513 
514 #if defined(BOOST_MSVC)
515 # pragma warning(pop)
516 #endif
517