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

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