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