• 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/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