test_callcc.cpp 13 KB

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