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