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

source code of boost/libs/context/test/test_callcc.cpp