test_fiber.cpp 14 KB

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