PageRenderTime 19ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/tests/fibers.cpp

http://github.com/mozy/mordor
C++ | 606 lines | 551 code | 47 blank | 8 comment | 160 complexity | f50a3f6e7873504ce9da02ca1fc025fa MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include <boost/bind.hpp>
  3. #include "mordor/fiber.h"
  4. #include "mordor/test/test.h"
  5. using namespace Mordor;
  6. using namespace Mordor::Test;
  7. struct DummyException : public boost::exception, public std::exception
  8. {
  9. ~DummyException() throw() {}
  10. };
  11. static void
  12. fiberProc1(Fiber::ptr mainFiber, Fiber::weak_ptr weakself, int &sequence)
  13. {
  14. Fiber::ptr self(weakself);
  15. ++sequence;
  16. MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
  17. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  18. MORDOR_TEST_ASSERT(mainFiber != self);
  19. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  20. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  21. Fiber::yield();
  22. ++sequence;
  23. MORDOR_TEST_ASSERT_EQUAL(sequence, 3);
  24. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  25. MORDOR_TEST_ASSERT(mainFiber != self);
  26. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  27. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  28. }
  29. MORDOR_UNITTEST(Fibers, call)
  30. {
  31. int sequence = 0;
  32. Fiber::ptr mainFiber = Fiber::getThis();
  33. Fiber::ptr a(new Fiber(NULL));
  34. a->reset(boost::bind(&fiberProc1, mainFiber, Fiber::weak_ptr(a), boost::ref(sequence)));
  35. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  36. MORDOR_TEST_ASSERT(a != mainFiber);
  37. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  38. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  39. a->call();
  40. ++sequence;
  41. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  42. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  43. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  44. MORDOR_TEST_ASSERT(a->state() == Fiber::HOLD);
  45. a->call();
  46. ++sequence;
  47. MORDOR_TEST_ASSERT_EQUAL(sequence, 4);
  48. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  49. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  50. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  51. }
  52. static void
  53. fiberProc2a(Fiber::ptr mainFiber, Fiber::weak_ptr weakself,
  54. Fiber::weak_ptr weakother, int &sequence)
  55. {
  56. Fiber::ptr self(weakself), other(weakother);
  57. ++sequence;
  58. MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
  59. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  60. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  61. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  62. MORDOR_TEST_ASSERT(other->state() == Fiber::INIT);
  63. other->call();
  64. ++sequence;
  65. MORDOR_TEST_ASSERT_EQUAL(sequence, 3);
  66. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  67. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  68. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  69. MORDOR_TEST_ASSERT(other->state() == Fiber::TERM);
  70. }
  71. static void
  72. fiberProc2b(Fiber::ptr mainFiber, Fiber::weak_ptr weakself,
  73. Fiber::weak_ptr weakother, int &sequence)
  74. {
  75. Fiber::ptr self(weakself), other(weakother);
  76. ++sequence;
  77. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  78. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  79. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  80. MORDOR_TEST_ASSERT(other->state() == Fiber::EXEC);
  81. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  82. }
  83. MORDOR_UNITTEST(Fibers, nestedCall)
  84. {
  85. int sequence = 0;
  86. Fiber::ptr mainFiber = Fiber::getThis();
  87. Fiber::ptr a(new Fiber(NULL));
  88. Fiber::ptr b(new Fiber(NULL));
  89. a->reset(boost::bind(&fiberProc2a, mainFiber, Fiber::weak_ptr(a),
  90. Fiber::weak_ptr(b), boost::ref(sequence)));
  91. b->reset(boost::bind(&fiberProc2b, mainFiber, Fiber::weak_ptr(b),
  92. Fiber::weak_ptr(a), boost::ref(sequence)));
  93. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  94. MORDOR_TEST_ASSERT(a != mainFiber);
  95. MORDOR_TEST_ASSERT(b != mainFiber);
  96. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  97. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  98. MORDOR_TEST_ASSERT(b->state() == Fiber::INIT);
  99. a->call();
  100. ++sequence;
  101. MORDOR_TEST_ASSERT_EQUAL(sequence, 4);
  102. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  103. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  104. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  105. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  106. }
  107. // Call graphs for this test:
  108. // main -> A -> B
  109. // (yieldTo C) C -> D
  110. // (yieldTo B), unwind to main
  111. // (yieldTo D), unwind to C
  112. // (implicit yieldTo main)
  113. static void
  114. fiberProc3a(Fiber::ptr mainFiber, Fiber::weak_ptr weaka, Fiber::weak_ptr weakb,
  115. Fiber::weak_ptr weakc, Fiber::weak_ptr weakd, int &sequence)
  116. {
  117. Fiber::ptr a(weaka), b(weakb), c(weakc), d(weakd);
  118. ++sequence;
  119. MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
  120. MORDOR_TEST_ASSERT(Fiber::getThis() == a);
  121. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  122. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  123. MORDOR_TEST_ASSERT(b->state() == Fiber::INIT);
  124. MORDOR_TEST_ASSERT(c->state() == Fiber::INIT);
  125. MORDOR_TEST_ASSERT(d->state() == Fiber::INIT);
  126. b->call();
  127. ++sequence;
  128. MORDOR_TEST_ASSERT_EQUAL(sequence, 6);
  129. MORDOR_TEST_ASSERT(Fiber::getThis() == a);
  130. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  131. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  132. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  133. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  134. MORDOR_TEST_ASSERT(d->state() == Fiber::HOLD);
  135. }
  136. static void
  137. fiberProc3b(Fiber::ptr mainFiber, Fiber::weak_ptr weaka, Fiber::weak_ptr weakb,
  138. Fiber::weak_ptr weakc, Fiber::weak_ptr weakd, int &sequence)
  139. {
  140. Fiber::ptr a(weaka), b(weakb), c(weakc), d(weakd);
  141. ++sequence;
  142. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  143. MORDOR_TEST_ASSERT(Fiber::getThis() == b);
  144. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  145. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  146. MORDOR_TEST_ASSERT(b->state() == Fiber::EXEC);
  147. MORDOR_TEST_ASSERT(c->state() == Fiber::INIT);
  148. MORDOR_TEST_ASSERT(d->state() == Fiber::INIT);
  149. c->yieldTo();
  150. ++sequence;
  151. MORDOR_TEST_ASSERT_EQUAL(sequence, 5);
  152. MORDOR_TEST_ASSERT(Fiber::getThis() == b);
  153. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  154. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  155. MORDOR_TEST_ASSERT(b->state() == Fiber::EXEC);
  156. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  157. MORDOR_TEST_ASSERT(d->state() == Fiber::HOLD);
  158. }
  159. static void
  160. fiberProc3c(Fiber::ptr mainFiber, Fiber::weak_ptr weaka, Fiber::weak_ptr weakb,
  161. Fiber::weak_ptr weakc, Fiber::weak_ptr weakd, int &sequence)
  162. {
  163. Fiber::ptr a(weaka), b(weakb), c(weakc), d(weakd);
  164. ++sequence;
  165. MORDOR_TEST_ASSERT_EQUAL(sequence, 3);
  166. MORDOR_TEST_ASSERT(Fiber::getThis() == c);
  167. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  168. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  169. MORDOR_TEST_ASSERT(b->state() == Fiber::HOLD);
  170. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  171. MORDOR_TEST_ASSERT(d->state() == Fiber::INIT);
  172. d->call();
  173. ++sequence;
  174. MORDOR_TEST_ASSERT_EQUAL(sequence, 9);
  175. MORDOR_TEST_ASSERT(Fiber::getThis() == c);
  176. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::HOLD);
  177. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  178. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  179. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  180. MORDOR_TEST_ASSERT(d->state() == Fiber::TERM);
  181. // Implicit yieldTo "caller"
  182. }
  183. static void
  184. fiberProc3d(Fiber::ptr mainFiber, Fiber::weak_ptr weaka, Fiber::weak_ptr weakb,
  185. Fiber::weak_ptr weakc, Fiber::weak_ptr weakd, int &sequence)
  186. {
  187. Fiber::ptr a(weaka), b(weakb), c(weakc), d(weakd);
  188. ++sequence;
  189. MORDOR_TEST_ASSERT_EQUAL(sequence, 4);
  190. MORDOR_TEST_ASSERT(Fiber::getThis() == d);
  191. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  192. MORDOR_TEST_ASSERT(a->state() == Fiber::EXEC);
  193. MORDOR_TEST_ASSERT(b->state() == Fiber::HOLD);
  194. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  195. MORDOR_TEST_ASSERT(d->state() == Fiber::EXEC);
  196. b->yieldTo();
  197. ++sequence;
  198. MORDOR_TEST_ASSERT_EQUAL(sequence, 8);
  199. MORDOR_TEST_ASSERT(Fiber::getThis() == d);
  200. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::HOLD);
  201. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  202. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  203. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  204. MORDOR_TEST_ASSERT(d->state() == Fiber::EXEC);
  205. }
  206. MORDOR_UNITTEST(Fibers, yieldTo)
  207. {
  208. int sequence = 0;
  209. Fiber::ptr mainFiber = Fiber::getThis();
  210. Fiber::ptr a(new Fiber(NULL));
  211. Fiber::ptr b(new Fiber(NULL));
  212. Fiber::ptr c(new Fiber(NULL));
  213. Fiber::ptr d(new Fiber(NULL));
  214. a->reset(boost::bind(&fiberProc3a, mainFiber, Fiber::weak_ptr(a),
  215. Fiber::weak_ptr(b), Fiber::weak_ptr(c),
  216. Fiber::weak_ptr(d), boost::ref(sequence)));
  217. b->reset(boost::bind(&fiberProc3b, mainFiber, Fiber::weak_ptr(a),
  218. Fiber::weak_ptr(b), Fiber::weak_ptr(c),
  219. Fiber::weak_ptr(d), boost::ref(sequence)));
  220. c->reset(boost::bind(&fiberProc3c, mainFiber, Fiber::weak_ptr(a),
  221. Fiber::weak_ptr(b), Fiber::weak_ptr(c),
  222. Fiber::weak_ptr(d), boost::ref(sequence)));
  223. d->reset(boost::bind(&fiberProc3d, mainFiber, Fiber::weak_ptr(a),
  224. Fiber::weak_ptr(b), Fiber::weak_ptr(c),
  225. Fiber::weak_ptr(d), boost::ref(sequence)));
  226. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  227. MORDOR_TEST_ASSERT(a != mainFiber);
  228. MORDOR_TEST_ASSERT(b != mainFiber);
  229. MORDOR_TEST_ASSERT(c != mainFiber);
  230. MORDOR_TEST_ASSERT(d != mainFiber);
  231. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  232. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  233. MORDOR_TEST_ASSERT(b->state() == Fiber::INIT);
  234. MORDOR_TEST_ASSERT(c->state() == Fiber::INIT);
  235. MORDOR_TEST_ASSERT(d->state() == Fiber::INIT);
  236. a->call();
  237. ++sequence;
  238. MORDOR_TEST_ASSERT_EQUAL(sequence, 7);
  239. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  240. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  241. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  242. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  243. MORDOR_TEST_ASSERT(c->state() == Fiber::EXEC);
  244. MORDOR_TEST_ASSERT(d->state() == Fiber::HOLD);
  245. d->yieldTo();
  246. ++sequence;
  247. MORDOR_TEST_ASSERT_EQUAL(sequence, 10);
  248. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  249. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  250. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  251. MORDOR_TEST_ASSERT(b->state() == Fiber::TERM);
  252. MORDOR_TEST_ASSERT(c->state() == Fiber::TERM);
  253. MORDOR_TEST_ASSERT(d->state() == Fiber::TERM);
  254. }
  255. static void fiberProcYieldBack(int &sequence, Fiber::ptr caller,
  256. Fiber::weak_ptr weakself)
  257. {
  258. Fiber::ptr self(weakself);
  259. MORDOR_TEST_ASSERT_EQUAL(++sequence, 1);
  260. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  261. MORDOR_TEST_ASSERT(caller != self);
  262. MORDOR_TEST_ASSERT(caller->state() == Fiber::HOLD);
  263. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  264. caller->yieldTo();
  265. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  266. MORDOR_TEST_ASSERT(caller != self);
  267. MORDOR_TEST_ASSERT(caller->state() == Fiber::EXEC);
  268. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  269. MORDOR_TEST_ASSERT_EQUAL(++sequence, 3);
  270. }
  271. MORDOR_UNITTEST(Fibers, yieldBackThenCall)
  272. {
  273. int sequence = 0;
  274. Fiber::ptr mainFiber = Fiber::getThis();
  275. Fiber::ptr a(new Fiber(NULL));
  276. a->reset(boost::bind(&fiberProcYieldBack, boost::ref(sequence),
  277. mainFiber, Fiber::weak_ptr(a)));
  278. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  279. MORDOR_TEST_ASSERT(mainFiber != a);
  280. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  281. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  282. a->yieldTo();
  283. MORDOR_TEST_ASSERT_EQUAL(++sequence, 2);
  284. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  285. MORDOR_TEST_ASSERT(mainFiber != a);
  286. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  287. MORDOR_TEST_ASSERT(a->state() == Fiber::HOLD);
  288. a->call();
  289. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  290. MORDOR_TEST_ASSERT(mainFiber != a);
  291. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  292. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  293. MORDOR_TEST_ASSERT_EQUAL(++sequence, 4);
  294. }
  295. static void
  296. fiberProc4(Fiber::ptr mainFiber, Fiber::weak_ptr weakself, int &sequence, bool exception)
  297. {
  298. Fiber::ptr self(weakself);
  299. MORDOR_TEST_ASSERT(Fiber::getThis() == self);
  300. MORDOR_TEST_ASSERT(mainFiber != self);
  301. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  302. MORDOR_TEST_ASSERT(self->state() == Fiber::EXEC);
  303. ++sequence;
  304. if (exception)
  305. MORDOR_THROW_EXCEPTION(DummyException());
  306. }
  307. MORDOR_UNITTEST(Fibers, reset)
  308. {
  309. int sequence = 0;
  310. Fiber::ptr mainFiber = Fiber::getThis();
  311. Fiber::ptr a(new Fiber(NULL));
  312. boost::function<void ()> dg = boost::bind(&fiberProc4, mainFiber, Fiber::weak_ptr(a),
  313. boost::ref(sequence), false);
  314. a->reset(dg);
  315. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  316. MORDOR_TEST_ASSERT(a != mainFiber);
  317. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  318. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  319. a->call();
  320. ++sequence;
  321. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  322. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  323. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  324. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  325. a->reset(dg);
  326. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  327. a->call();
  328. ++sequence;
  329. MORDOR_TEST_ASSERT_EQUAL(sequence, 4);
  330. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  331. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  332. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  333. a->reset(dg);
  334. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  335. a->call();
  336. ++sequence;
  337. MORDOR_TEST_ASSERT_EQUAL(sequence, 6);
  338. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  339. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  340. MORDOR_TEST_ASSERT(a->state() == Fiber::TERM);
  341. dg = boost::bind(&fiberProc4, mainFiber, Fiber::weak_ptr(a),
  342. boost::ref(sequence), true);
  343. a->reset(dg);
  344. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  345. MORDOR_TEST_ASSERT_EXCEPTION(a->call(), DummyException);
  346. ++sequence;
  347. MORDOR_TEST_ASSERT_EQUAL(sequence, 8);
  348. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  349. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  350. MORDOR_TEST_ASSERT(a->state() == Fiber::EXCEPT);
  351. a->reset(dg);
  352. MORDOR_TEST_ASSERT(a->state() == Fiber::INIT);
  353. MORDOR_TEST_ASSERT_EXCEPTION(a->call(), DummyException);
  354. ++sequence;
  355. MORDOR_TEST_ASSERT_EQUAL(sequence, 10);
  356. MORDOR_TEST_ASSERT(Fiber::getThis() == mainFiber);
  357. MORDOR_TEST_ASSERT(mainFiber->state() == Fiber::EXEC);
  358. MORDOR_TEST_ASSERT(a->state() == Fiber::EXCEPT);
  359. }
  360. static void throwBadAlloc()
  361. {
  362. MORDOR_THROW_EXCEPTION(std::bad_alloc());
  363. }
  364. MORDOR_UNITTEST(Fibers, badAlloc)
  365. {
  366. Fiber::ptr fiber(new Fiber(&throwBadAlloc));
  367. MORDOR_TEST_ASSERT_EXCEPTION(fiber->call(), std::bad_alloc);
  368. }
  369. static void throwFileNotFound()
  370. {
  371. MORDOR_THROW_EXCEPTION(FileNotFoundException());
  372. }
  373. MORDOR_UNITTEST(Fibers, nativeException)
  374. {
  375. Fiber::ptr fiber(new Fiber(&throwFileNotFound));
  376. MORDOR_TEST_ASSERT_EXCEPTION(fiber->call(), FileNotFoundException);
  377. }
  378. static void throwAccessDenied()
  379. {
  380. MORDOR_THROW_EXCEPTION(AccessDeniedException());
  381. }
  382. MORDOR_UNITTEST(Fibers, nativeException2)
  383. {
  384. Fiber::ptr fiber(new Fiber(&throwAccessDenied));
  385. MORDOR_TEST_ASSERT_EXCEPTION(fiber->call(), AccessDeniedException);
  386. }
  387. static void throwRuntimeError()
  388. {
  389. throw std::runtime_error("message");
  390. }
  391. MORDOR_UNITTEST(Fibers, runtimeError)
  392. {
  393. Fiber::ptr fiber(new Fiber(&throwRuntimeError));
  394. try {
  395. fiber->call();
  396. } catch (std::runtime_error &ex) {
  397. MORDOR_TEST_ASSERT_EQUAL(std::string(ex.what()), "message");
  398. }
  399. }
  400. MORDOR_UNITTEST(Fibers, badAllocYieldTo)
  401. {
  402. Fiber::ptr fiber(new Fiber(&throwBadAlloc));
  403. MORDOR_TEST_ASSERT_EXCEPTION(fiber->yieldTo(), std::bad_alloc);
  404. }
  405. static void throwGenericException()
  406. {
  407. MORDOR_THROW_EXCEPTION(DummyException());
  408. }
  409. MORDOR_UNITTEST(Fibers, genericException)
  410. {
  411. Fiber::ptr fiber(new Fiber(&throwGenericException));
  412. MORDOR_TEST_ASSERT_EXCEPTION(fiber->call(), DummyException);
  413. }
  414. MORDOR_UNITTEST(Fibers, fiberThrowingExceptionOutOfScope)
  415. {
  416. try {
  417. Fiber::ptr fiber(new Fiber(&throwGenericException));
  418. fiber->call();
  419. MORDOR_NOTREACHED();
  420. } catch (DummyException &) {
  421. }
  422. }
  423. MORDOR_UNITTEST(Fibers, forceThrowExceptionNewFiberCall)
  424. {
  425. Fiber::ptr f(new Fiber(&throwRuntimeError));
  426. boost::exception_ptr exception;
  427. try {
  428. throw boost::enable_current_exception(DummyException());
  429. } catch (...) {
  430. exception = boost::current_exception();
  431. }
  432. MORDOR_TEST_ASSERT_EXCEPTION(f->inject(exception), DummyException);
  433. }
  434. static void catchAndThrowDummy()
  435. {
  436. try {
  437. Fiber::yield();
  438. MORDOR_NOTREACHED();
  439. } catch (DummyException &) {
  440. throw;
  441. }
  442. MORDOR_NOTREACHED();
  443. }
  444. MORDOR_UNITTEST(Fibers, forceThrowExceptionFiberYield)
  445. {
  446. Fiber::ptr f(new Fiber(&catchAndThrowDummy));
  447. boost::exception_ptr exception;
  448. try {
  449. throw boost::enable_current_exception(DummyException());
  450. } catch (...) {
  451. exception = boost::current_exception();
  452. }
  453. f->call();
  454. MORDOR_TEST_ASSERT_EXCEPTION(f->inject(exception), DummyException);
  455. }
  456. static void catchAndThrowDummyYieldTo(Fiber::ptr caller)
  457. {
  458. try {
  459. caller->yieldTo();
  460. MORDOR_NOTREACHED();
  461. } catch (DummyException &) {
  462. throw;
  463. }
  464. MORDOR_NOTREACHED();
  465. }
  466. MORDOR_UNITTEST(Fibers, forceThrowExceptionFiberYieldTo)
  467. {
  468. Fiber::ptr mainFiber = Fiber::getThis();
  469. Fiber::ptr f(new Fiber(boost::bind(&catchAndThrowDummyYieldTo,
  470. mainFiber)));
  471. boost::exception_ptr exception;
  472. try {
  473. throw boost::enable_current_exception(DummyException());
  474. } catch (...) {
  475. exception = boost::current_exception();
  476. }
  477. f->yieldTo();
  478. MORDOR_TEST_ASSERT_EXCEPTION(f->inject(exception), DummyException);
  479. f->reset(NULL);
  480. }
  481. static void eatSomeStack()
  482. {
  483. char stackEater[4096];
  484. stackEater[0] = 1;
  485. (void) stackEater;
  486. }
  487. MORDOR_UNITTEST(Fibers, resetStress)
  488. {
  489. Fiber::ptr f(new Fiber(&eatSomeStack));
  490. for (int i = 0; i < 1025; ++i) {
  491. f->call();
  492. f->reset(&eatSomeStack);
  493. }
  494. }
  495. static void gimmeYourFiber(Fiber::ptr &threadFiber)
  496. {
  497. threadFiber = Fiber::getThis();
  498. }
  499. MORDOR_UNITTEST(Fibers, threadFiberHeldAfterThreadEnd)
  500. {
  501. Fiber::ptr threadFiber;
  502. Thread thread(boost::bind(&gimmeYourFiber, boost::ref(threadFiber)));
  503. thread.join();
  504. }
  505. namespace {
  506. struct LastObject
  507. {
  508. LastObject(Fiber::ptr mainFiber, int &sequence)
  509. : m_mainFiber(mainFiber),
  510. m_sequence(sequence)
  511. {
  512. MORDOR_TEST_ASSERT_EQUAL(++sequence, 3);
  513. }
  514. ~LastObject()
  515. {
  516. MORDOR_TEST_ASSERT(Fiber::getThis() == m_mainFiber.lock());
  517. MORDOR_TEST_ASSERT_EQUAL(++m_sequence, 6);
  518. }
  519. private:
  520. Fiber::weak_ptr m_mainFiber;
  521. int &m_sequence;
  522. };
  523. struct ExceptionDestructsBeforeFiberDestructsException : virtual Exception
  524. {
  525. ExceptionDestructsBeforeFiberDestructsException(Fiber::ptr mainFiber,
  526. int &sequence)
  527. : m_lastObject(new LastObject(mainFiber, sequence))
  528. {}
  529. ~ExceptionDestructsBeforeFiberDestructsException() throw() {}
  530. private:
  531. boost::shared_ptr<LastObject> m_lastObject;
  532. };
  533. }
  534. static void exceptionDestructsBeforeFiberDestructs(Fiber::ptr mainFiber,
  535. int &sequence)
  536. {
  537. MORDOR_TEST_ASSERT_EQUAL(++sequence, 2);
  538. MORDOR_THROW_EXCEPTION(ExceptionDestructsBeforeFiberDestructsException(
  539. mainFiber, sequence));
  540. }
  541. MORDOR_UNITTEST(Fibers, exceptionDestructsBeforeFiberDestructs)
  542. {
  543. int sequence = 0;
  544. {
  545. Fiber::ptr mainFiber = Fiber::getThis();
  546. Fiber::ptr throwingFiber(new Fiber(boost::bind(
  547. &exceptionDestructsBeforeFiberDestructs, mainFiber,
  548. boost::ref(sequence))));
  549. MORDOR_TEST_ASSERT_EQUAL(++sequence, 1);
  550. try {
  551. throwingFiber->call();
  552. MORDOR_NOTREACHED();
  553. } catch (ExceptionDestructsBeforeFiberDestructsException &) {
  554. MORDOR_TEST_ASSERT_EQUAL(++sequence, 4);
  555. }
  556. MORDOR_TEST_ASSERT_EQUAL(++sequence, 5);
  557. }
  558. MORDOR_TEST_ASSERT_EQUAL(++sequence, 7);
  559. }