1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
/*
* A test program for WvCont (continuable callbacks).
*/
#include "wvcont.h"
#include <stdio.h>
static void *nonfunc(void *_x)
{
long x = (long)_x;
return (void *)(1234560000 + x);
}
static void *func(void *_x)
{
long x = (long)_x;
for (int count = 0; count < 4 && WvCont::isok(); count++)
WvCont::yield((void *)++x);
return (void *) -(++x);
}
class Honk
{
public:
const char *id;
WvContCallback cb;
Honk(const char *_id)
{ id = _id; }
void honk_at(Honk &a)
{
cb = WvCont(wv::bind(&Honk::honker, this, a, _1));
}
private:
void *honker(Honk &h, void *_x)
{
long x = (long)_x;
printf("%s: STARTING (%ld)\n", id, x);
for (x--; WvCont::isok() && x > 0; x--)
{
printf("%s: --> Honking in (%ld)\n", id, x);
h.cb((void *)x);
printf("%s: <-- Honking out (%ld)\n", id, x);
}
printf("%s: DONE\n", id);
return (void *)x;
}
};
int main()
{
typedef wv::function<void*(void*)> CbType;
// basic functionality (including nested tasks)
{
CbType cbx = func; // not runnable itself: no yield allowed
CbType cb1 = WvCont(cbx); // a subtask
CbType cb2 = WvCont(cb1); // another subtask on top
CbType cb3 = cb2; // a copy of the second subtask
// note that in the above, there's really only one context in which
// 'func' actually gets called; there are no parallel running 'func's.
// cb2's task calls into cb1's task, however.
printf("zot1: %ld\n", (long)cb1((void *)100));
printf("zot1: %ld\n", (long)cb2((void *)200));
printf("zot1: %ld\n", (long)cb3((void *)300));
cb1 = WvCont(nonfunc);
printf("zot2: %ld\n", (long)cb1((void *)400));
printf("zot2: %ld\n", (long)cb2((void *)500));
printf("zot2: %ld\n", (long)cb3((void *)600));
cb2 = nonfunc;
printf("zot3: %ld\n", (long)cb1((void *)700));
printf("zot3: %ld\n", (long)cb2((void *)800));
printf("zot3: %ld\n", (long)cb3((void *)900));
cb3 = nonfunc;
printf("zot4: %ld\n", (long)cb1((void *)1000));
printf("zot4: %ld\n", (long)cb2((void *)1100));
printf("zot4: %ld\n", (long)cb3((void *)1200));
}
// fun with recursive continuations. If this doesn't do something
// predictable, we'll get screwy bugs when we use this in WvStreams - just
// like we did with the pre-WvCont continue_select() implementation.
//
// The *desired* behaviour here is the same as with real recursive
// function calls: if a calls b who calls c, and then c calls a again,
// then a should do its thing, return (or yield), then c will finish,
// yield, then b will finish, yield, and then a will have a chance to run
// again.
//
// In old wvstreams, we would silently short-circuit the recursion (the
// inner a would yield immediately without doing anything). This is
// easy to implement, but causes problems if c actually expects a to do
// something.
//
// Unfortunately, the semantics of this are tricky with continuations:
// when we call the inner a, we re-enter its context, but that context
// is waiting for b to return. It can't do anything unless b returns,
// so what can we do?
//
// ...we assert() instead. So expect an assertion failure below.
printf("Expect an assertion failure shortly!\n");
{
Honk h1("honk1"), h2("honk2"), h3("honk3");
h1.honk_at(h2);
h2.honk_at(h3);
h3.honk_at(h1);
h1.cb((void *)5);
}
return 0;
}
|