Imported Upstream version 1.71.0
[platform/upstream/boost.git] / libs / context / test / test_execution_context_v2.cpp
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 <iostream>
8 #include <memory>
9 #include <sstream>
10 #include <stdexcept>
11 #include <string>
12 #include <thread>
13 #include <utility>
14
15 #include <boost/array.hpp>
16 #include <boost/assert.hpp>
17 #include <boost/lexical_cast.hpp>
18 #include <boost/test/unit_test.hpp>
19 #include <boost/utility.hpp>
20 #include <boost/variant.hpp>
21
22 #include <boost/context/execution_context.hpp>
23 #include <boost/context/detail/config.hpp>
24
25 #ifdef BOOST_WINDOWS
26 #include <windows.h>
27 #endif
28
29 #if defined(BOOST_MSVC)
30 # pragma warning(push)
31 # pragma warning(disable: 4723)
32 #endif
33
34 typedef boost::variant<int,std::string> variant_t;
35
36 namespace ctx = boost::context;
37
38 int value1 = 0;
39 std::string value2;
40 double value3 = 0.;
41
42 struct X {
43     ctx::execution_context< void > foo( int i, ctx::execution_context< void > && ctx) {
44         value1 = i;
45         return std::move( ctx);
46     }
47 };
48
49 struct Y {
50     Y() {
51         value1 = 3;
52     }
53
54     Y( Y const&) = delete;
55     Y & operator=( Y const&) = delete;
56
57     ~Y() {
58         value1 = 7;
59     }
60 };
61
62 class moveable {
63 public:
64     bool    state;
65     int     value;
66
67     moveable() :
68         state( false),
69         value( -1) {
70     }
71
72     moveable( int v) :
73         state( true),
74         value( v) {
75     }
76
77     moveable( moveable && other) :
78         state( other.state),
79         value( other.value) {
80         other.state = false;
81         other.value = -1;
82     }
83
84     moveable & operator=( moveable && other) {
85         if ( this == & other) return * this;
86         state = other.state;
87         value = other.value;
88         other.state = false;
89         other.value = -1;
90         return * this;
91     }
92
93     moveable( moveable const& other) = delete;
94     moveable & operator=( moveable const& other) = delete;
95
96     void operator()() {
97         value1 = value;
98     }
99 };
100
101 struct my_exception : public std::runtime_error {
102     my_exception( char const* what) :
103         std::runtime_error( what) {
104     }
105 };
106
107 #ifdef BOOST_MSVC
108 // Optimizations can remove the integer-divide-by-zero here.
109 #pragma optimize("", off)
110 void seh( bool & catched) {
111     __try {
112         int i = 1;
113         i /= 0;
114     } __except( EXCEPTION_EXECUTE_HANDLER) {
115         catched = true;
116     }
117 }
118 #pragma optimize("", on)
119 #endif
120
121 ctx::execution_context< void > fn1( int i, ctx::execution_context< void > && ctx) {
122     value1 = i;
123     return std::move( ctx);
124 }
125
126 ctx::execution_context< void > fn2( const char * what, ctx::execution_context< void > && ctx) {
127     try {
128         throw std::runtime_error( what);
129     } catch ( std::runtime_error const& e) {
130         value2 = e.what();
131     }
132     return std::move( ctx);
133 }
134
135 ctx::execution_context< void > fn3( double d, ctx::execution_context< void > && ctx) {
136     d += 3.45;
137     value3 = d;
138     return std::move( ctx);
139 }
140
141 ctx::execution_context< void > fn5( ctx::execution_context< void > && ctx) {
142     value1 = 3;
143     return std::move( ctx);
144 }
145
146 ctx::execution_context< void > fn4( ctx::execution_context< void > && ctx) {
147     ctx::execution_context< void > ctx1( fn5);
148     ctx1();
149     value3 = 3.14;
150     return std::move( ctx);
151 }
152
153 ctx::execution_context< void > fn6( ctx::execution_context< void > && ctx) {
154     Y y;
155     try {
156         value1 = 3;
157         value3 = 4.;
158         ctx = ctx();
159         value1 = 7;
160         value3 = 8.;
161         ctx = ctx();
162     } catch ( my_exception & e) {
163         value2 = e.what();
164     }
165     return std::move( ctx);
166 }
167
168 ctx::execution_context< void > fn7( ctx::execution_context< void > && ctx) {
169     Y y;
170     return ctx();
171 }
172
173 ctx::execution_context< int > fn8( ctx::execution_context< int > && ctx, int i) {
174     value1 = i;
175     return std::move( ctx);
176 }
177
178 ctx::execution_context< int > fn9( ctx::execution_context< int > && ctx, int i) {
179     value1 = i;
180     std::tie( ctx, i) = ctx( i);
181     value1 = i;
182     return std::move( ctx);
183 }
184
185 ctx::execution_context< int & > fn10( ctx::execution_context< int & > && ctx, int & i) {
186     std::tie( ctx, i) = ctx( i);
187     return std::move( ctx);
188 }
189
190 ctx::execution_context< moveable > fn11( ctx::execution_context< moveable > && ctx, moveable m) {
191     std::tie( ctx, m) = ctx( std::move( m) );
192     return std::move( ctx);
193 }
194
195 ctx::execution_context< int, std::string > fn12( ctx::execution_context< int, std::string > && ctx, int i, std::string str) {
196     std::tie( ctx, i, str) = ctx( i, str);
197     return std::move( ctx);
198 }
199
200 ctx::execution_context< int, moveable > fn13( ctx::execution_context< int, moveable > && ctx, int i, moveable m) {
201     std::tie( ctx, i, m) = ctx( i, std::move( m) );
202     return std::move( ctx);
203 }
204
205 ctx::execution_context< variant_t > fn14( ctx::execution_context< variant_t > && ctx, variant_t data) {
206     int i = boost::get< int >( data);
207     data = boost::lexical_cast< std::string >( i);
208     std::tie( ctx, data) = ctx( data);
209     return std::move( ctx);
210 }
211
212 ctx::execution_context< Y * > fn15( ctx::execution_context< Y * > && ctx, Y * py) {
213     ctx( py);
214     return std::move( ctx);
215 }
216
217 ctx::execution_context< int > fn16( ctx::execution_context< int > && ctx, int i) {
218     value1 = i;
219     std::tie( ctx, i) = ctx( i);
220     value1 = i;
221     return std::move( ctx);
222 }
223
224 ctx::execution_context< int, int > fn17( ctx::execution_context< int, int > && ctx, int i, int j) {
225     for (;;) {
226         std::tie( ctx, i, j) = ctx( i, j);
227     }
228     return std::move( ctx);
229 }
230
231 ctx::execution_context< void > fn18( ctx::execution_context< void > && ctx) {
232     Y y;
233     value3 = 2.;
234     ctx = ctx();
235     try {
236         value3 = 3.;
237         ctx = ctx();
238     } catch ( boost::context::detail::forced_unwind const&) {
239         value3 = 4.;
240         throw;
241     } catch (...) {
242         value3 = 5.;
243     }
244     value3 = 6.;
245     return std::move( ctx);
246 }
247
248 ctx::execution_context< void > fn19( ctx::execution_context< void > && ctx) {
249     Y y;
250     try {
251         value3 = 3.;
252         ctx = ctx();
253     } catch (...) {
254         value3 = 5.;
255     }
256     return std::move( ctx);
257 }
258
259
260 void test_move() {
261     value1 = 0;
262     ctx::execution_context< void > ctx;
263     BOOST_CHECK( ! ctx);
264     ctx::execution_context< void > ctx1( fn1, 1);
265     ctx::execution_context< void > ctx2( fn1, 3);
266     BOOST_CHECK( ctx1);
267     BOOST_CHECK( ctx2);
268     ctx1 = std::move( ctx2);
269     BOOST_CHECK( ctx1);
270     BOOST_CHECK( ! ctx2);
271     BOOST_CHECK_EQUAL( 0, value1);
272     ctx1();
273     BOOST_CHECK_EQUAL( 3, value1);
274     BOOST_CHECK( ! ctx1);
275     BOOST_CHECK( ! ctx2);
276 }
277
278 void test_memfn() {
279     value1 = 0;
280     X x;
281     ctx::execution_context< void > ctx( & X::foo, x, 7);
282     ctx();
283     BOOST_CHECK_EQUAL( 7, value1);
284 }
285
286 void test_exception() {
287     {
288         const char * what = "hello world";
289         ctx::execution_context< void > ctx( fn2, what);
290         BOOST_CHECK( ctx);
291         ctx();
292         BOOST_CHECK_EQUAL( std::string( what), value2);
293         BOOST_CHECK( ! ctx);
294     }
295 #ifdef BOOST_MSVC
296     {
297         bool catched = false;
298         std::thread([&catched](){
299                 ctx::execution_context< void > ctx([&catched](ctx::execution_context< void > && ctx){
300                         seh( catched);
301                         return std::move( ctx);
302                         });
303             BOOST_CHECK( ctx);
304             ctx();
305         }).join();
306         BOOST_CHECK( catched);
307     }
308 #endif
309 }
310
311 void test_fp() {
312     double d = 7.13;
313     ctx::execution_context< void > ctx( fn3, d);
314     BOOST_CHECK( ctx);
315     ctx();
316     BOOST_CHECK_EQUAL( 10.58, value3);
317     BOOST_CHECK( ! ctx);
318 }
319
320 void test_stacked() {
321     value1 = 0;
322     value3 = 0.;
323     ctx::execution_context< void > ctx( fn4);
324     BOOST_CHECK( ctx);
325     ctx();
326     BOOST_CHECK_EQUAL( 3, value1);
327     BOOST_CHECK_EQUAL( 3.14, value3);
328     BOOST_CHECK( ! ctx);
329 }
330
331 void test_prealloc() {
332     value1 = 0;
333     ctx::default_stack alloc;
334     ctx::stack_context sctx( alloc.allocate() );
335     void * sp = static_cast< char * >( sctx.sp) - 10;
336     std::size_t size = sctx.size - 10;
337     ctx::execution_context< void > ctx( std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, fn1, 7);
338     BOOST_CHECK( ctx);
339     ctx();
340     BOOST_CHECK_EQUAL( 7, value1);
341     BOOST_CHECK( ! ctx);
342 }
343
344 void test_ontop() {
345     {
346         int i = 3, j = 0;
347         ctx::execution_context< int > ctx([]( ctx::execution_context< int > && ctx, int x) {
348                     for (;;) {
349                         std::tie( ctx, x) = ctx( x*10);
350                     }
351                     return std::move( ctx);
352                 });
353         std::tie( ctx, j) = ctx( ctx::exec_ontop_arg,
354                 []( int x){
355                     return x-10;
356                 },
357                 i);
358         BOOST_CHECK( ctx);
359         BOOST_CHECK_EQUAL( j, -70);
360     }
361     {
362         int i = 3, j = 1;
363         ctx::execution_context< int, int > ctx( fn17);
364         std::tie( ctx, i, j) = ctx( i, j);
365         std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg,
366                 []( int x, int y) {
367                     return std::make_tuple( x - y, x + y);
368                 },
369                 i, j);
370         BOOST_CHECK_EQUAL( i, 2);
371         BOOST_CHECK_EQUAL( j, 4);
372     }
373     {
374         moveable m1( 7), m2;
375         BOOST_CHECK( 7 == m1.value);
376         BOOST_CHECK( m1.state);
377         BOOST_CHECK( -1 == m2.value);
378         BOOST_CHECK( ! m2.state);
379         ctx::execution_context< moveable > ctx( fn11);
380         std::tie( ctx, m2) = ctx( ctx::exec_ontop_arg,
381                 []( moveable&& m){
382                     return std::move( m);
383                 },
384                 std::move( m1) );
385         BOOST_CHECK( -1 == m1.value);
386         BOOST_CHECK( ! m1.state);
387         BOOST_CHECK( 7 == m2.value);
388         BOOST_CHECK( m2.state);
389     }
390 }
391
392 void test_ontop_exception() {
393     {
394         value1 = 0;
395         value2 = "";
396         ctx::execution_context< void > ctx([](ctx::execution_context< void > && ctx){
397                 for (;;) {
398                     value1 = 3;
399                     try {
400                         ctx = ctx();
401                     } catch ( ctx::ontop_error const& e) {
402                         try {
403                             std::rethrow_if_nested( e);
404                         } catch ( my_exception const& ex) {
405                             value2 = ex.what();
406                         }
407                         return e.get_context< void >();
408                     }
409                 }
410                 return std::move( ctx);
411         });
412         ctx = ctx();
413         BOOST_CHECK_EQUAL( 3, value1);
414         const char * what = "hello world";
415         ctx( ctx::exec_ontop_arg,
416              [what](){
417                 throw my_exception( what);
418              });
419         BOOST_CHECK_EQUAL( 3, value1);
420         BOOST_CHECK_EQUAL( std::string( what), value2);
421     }
422     {
423         value2 = "";
424         int i = 3, j = 1;
425         ctx::execution_context< int, int > ctx([]( ctx::execution_context< int, int > && ctx, int x, int y) {
426                 for (;;) {
427                     try {
428                             std::tie( ctx, x, y) = ctx( x+y,x-y);
429                     } catch ( ctx::ontop_error const& e) {
430                         try {
431                             std::rethrow_if_nested( e);
432                         } catch ( my_exception const& ex) {
433                             value2 = ex.what();
434                         }
435                         return e.get_context< int, int >();
436                     }
437                 }
438                 return std::move( ctx);
439         });
440         std::tie( ctx, i, j) = ctx( i, j);
441         BOOST_CHECK( ctx);
442         BOOST_CHECK_EQUAL( i, 4);
443         BOOST_CHECK_EQUAL( j, 2);
444         const char * what = "hello world";
445         std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg,
446                 [what](int x, int y) {
447                     throw my_exception(what);
448                     return std::make_tuple( x*y, x/y);
449                 },
450                 i, j);
451         BOOST_CHECK_EQUAL( i, 4);
452         BOOST_CHECK_EQUAL( j, 2);
453         BOOST_CHECK_EQUAL( std::string( what), value2);
454     }
455 }
456
457 void test_termination1() {
458     {
459         value1 = 0;
460         ctx::execution_context< void > ctx( fn7);
461         BOOST_CHECK_EQUAL( 0, value1);
462         ctx = ctx();
463         BOOST_CHECK_EQUAL( 3, value1);
464     }
465     BOOST_CHECK_EQUAL( 7, value1);
466     {
467         value1 = 0;
468         BOOST_CHECK_EQUAL( 0, value1);
469         ctx::execution_context< void > ctx( fn5);
470         BOOST_CHECK( ctx);
471         ctx();
472         BOOST_CHECK_EQUAL( 3, value1);
473         BOOST_CHECK( ! ctx);
474     }
475     {
476         value1 = 0;
477         BOOST_CHECK_EQUAL( 0, value1);
478         int i = 3, j = 0;
479         ctx::execution_context< int > ctx( fn9);
480         BOOST_CHECK( ctx);
481         std::tie( ctx, j) = ctx( i);
482         BOOST_CHECK_EQUAL( i, value1);
483         BOOST_CHECK( ctx);
484         BOOST_CHECK_EQUAL( i, j);
485         i = 7;
486         std::tie( ctx, j) = ctx( i);
487         BOOST_CHECK_EQUAL( i, value1);
488         BOOST_CHECK( ! ctx);
489         BOOST_CHECK_EQUAL( i, j);
490     }
491 }
492
493 void test_termination2() {
494     {
495         value1 = 0;
496         value3 = 0.;
497         ctx::execution_context< void > ctx( fn6);
498         BOOST_CHECK_EQUAL( 0, value1);
499         BOOST_CHECK_EQUAL( 0., value3);
500         ctx = ctx();
501         BOOST_CHECK_EQUAL( 3, value1);
502         BOOST_CHECK_EQUAL( 4., value3);
503     }
504     BOOST_CHECK_EQUAL( 7, value1);
505     BOOST_CHECK_EQUAL( 4., value3);
506 }
507
508 void test_one_arg() {
509     {
510         value1 = 0;
511         ctx::execution_context< int > ctx( fn8);
512         ctx( 7);
513         BOOST_CHECK_EQUAL( 7, value1);
514     }
515     {
516         int i = 3, j = 0;
517         ctx::execution_context< int > ctx( fn9);
518         std::tie( ctx, j) = ctx( i);
519         BOOST_CHECK_EQUAL( i, j);
520     }
521     {
522         int i = 3, j = 0;
523         int & k = j;
524         BOOST_CHECK( & i != & k);
525         BOOST_CHECK( & j == & k);
526         ctx::execution_context< int & > ctx( fn10);
527         std::tie( ctx, k) = ctx( i);
528         BOOST_CHECK( & i != & k);
529     }
530     {
531         Y y;
532         Y * py = nullptr;
533         ctx::execution_context< Y * > ctx( fn15);
534         std::tie( ctx, py) = ctx( & y);
535         BOOST_CHECK( py == & y);
536     }
537     {
538         moveable m1( 7), m2;
539         BOOST_CHECK( 7 == m1.value);
540         BOOST_CHECK( m1.state);
541         BOOST_CHECK( -1 == m2.value);
542         BOOST_CHECK( ! m2.state);
543         ctx::execution_context< moveable > ctx( fn11);
544         std::tie( ctx, m2) = ctx( std::move( m1) );
545         BOOST_CHECK( -1 == m1.value);
546         BOOST_CHECK( ! m1.state);
547         BOOST_CHECK( 7 == m2.value);
548         BOOST_CHECK( m2.state);
549     }
550 }
551
552 void test_two_args() {
553     {
554         int i1 = 3, i2 = 0;
555         std::string str1("abc"), str2;
556         ctx::execution_context< int, std::string > ctx( fn12);
557         std::tie( ctx, i2, str2) = ctx( i1, str1);
558         BOOST_CHECK_EQUAL( i1, i2);
559         BOOST_CHECK_EQUAL( str1, str2);
560     }
561     {
562         int i1 = 3, i2 = 0;
563         moveable m1( 7), m2;
564         BOOST_CHECK( 7 == m1.value);
565         BOOST_CHECK( m1.state);
566         BOOST_CHECK( -1 == m2.value);
567         BOOST_CHECK( ! m2.state);
568         ctx::execution_context< int, moveable > ctx( fn13);
569         std::tie( ctx, i2, m2) = ctx( i1, std::move( m1) );
570         BOOST_CHECK_EQUAL( i1, i2);
571         BOOST_CHECK( -1 == m1.value);
572         BOOST_CHECK( ! m1.state);
573         BOOST_CHECK( 7 == m2.value);
574         BOOST_CHECK( m2.state);
575     }
576 }
577
578 void test_variant() {
579     {
580         int i = 7;
581         variant_t data1 = i, data2;
582         ctx::execution_context< variant_t > ctx( fn14);
583         std::tie( ctx, data2) = ctx( data1);
584         std::string str = boost::get< std::string >( data2);
585         BOOST_CHECK_EQUAL( std::string("7"), str);
586     }
587 }
588
589 #ifdef BOOST_WINDOWS
590 void test_bug12215() {
591         ctx::execution_context< void > ctx(
592             [](ctx::execution_context< void > && ctx) {
593                 char buffer[MAX_PATH];
594                 GetModuleFileName( nullptr, buffer, MAX_PATH);
595                 return std::move( ctx);
596             });
597         ctx();
598
599 }
600 #endif
601
602 void test_goodcatch() {
603     value1 = 0;
604     value3 = 0.;
605     {
606         ctx::execution_context< void > ctx( fn18);
607         BOOST_CHECK_EQUAL( 0, value1);
608         BOOST_CHECK_EQUAL( 0., value3);
609         ctx = ctx();
610         BOOST_CHECK_EQUAL( 3, value1);
611         BOOST_CHECK_EQUAL( 2., value3);
612         ctx = ctx();
613         BOOST_CHECK_EQUAL( 3, value1);
614         BOOST_CHECK_EQUAL( 3., value3);
615     }
616     BOOST_CHECK_EQUAL( 7, value1);
617     BOOST_CHECK_EQUAL( 4., value3);
618 }
619
620 void test_badcatch() {
621 #if 0
622     value1 = 0;
623     value3 = 0.;
624     {
625         ctx::execution_context< void > * ctx = new ctx::execution_context< void >( fn19);
626         BOOST_CHECK_EQUAL( 0, value1);
627         BOOST_CHECK_EQUAL( 0., value3);
628         (*ctx) = (*ctx)();
629         BOOST_CHECK_EQUAL( 3, value1);
630         BOOST_CHECK_EQUAL( 3., value3);
631         // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught
632         // in fn19.  That will trigger the "not caught" assertion in ~forced_unwind.  Getting that 
633         // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good
634         // way to hook directly into the assertion when it happens on an alternate stack.
635         delete ctx;
636     }
637     BOOST_CHECK_EQUAL( 7, value1);
638     BOOST_CHECK_EQUAL( 4., value3);
639 #endif
640 }
641
642 boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
643 {
644     boost::unit_test::test_suite * test =
645         BOOST_TEST_SUITE("Boost.Context: execution_context v2 test suite");
646
647     test->add( BOOST_TEST_CASE( & test_move) );
648     test->add( BOOST_TEST_CASE( & test_memfn) );
649     test->add( BOOST_TEST_CASE( & test_exception) );
650     test->add( BOOST_TEST_CASE( & test_fp) );
651     test->add( BOOST_TEST_CASE( & test_stacked) );
652     test->add( BOOST_TEST_CASE( & test_prealloc) );
653     test->add( BOOST_TEST_CASE( & test_ontop) );
654     test->add( BOOST_TEST_CASE( & test_ontop_exception) );
655     test->add( BOOST_TEST_CASE( & test_termination1) );
656     test->add( BOOST_TEST_CASE( & test_termination2) );
657     test->add( BOOST_TEST_CASE( & test_one_arg) );
658     test->add( BOOST_TEST_CASE( & test_two_args) );
659     test->add( BOOST_TEST_CASE( & test_variant) );
660 #ifdef BOOST_WINDOWS
661     test->add( BOOST_TEST_CASE( & test_bug12215) );
662 #endif
663     test->add( BOOST_TEST_CASE( & test_goodcatch) );
664     test->add( BOOST_TEST_CASE( & test_badcatch) );
665
666     return test;
667 }
668
669 #if defined(BOOST_MSVC)
670 # pragma warning(pop)
671 #endif