+ pgen_event ev;
+ int rq, rc;
+ pgen_proc *proc;
+ void *ctx;
+ int p;
+
+ enum { P_STEP, P_TEST };
+
+ /* --- Set up the initial event block --- */
+
+ ev.name = name;
+ if (m)
+ ev.m = MP_COPY(m);
+ else
+ ev.m = 0;
+ ev.steps = 0;
+ ev.tests = 0;
+ ev.r = fibrand_create(0);
+
+ /* --- Tell the event handler we're under way --- */
+
+ if (event && event(PGEN_BEGIN, &ev, ectx) == PGEN_ABORT) {
+ ev.r->ops->destroy(ev.r);
+ return (0);
+ }
+
+ /* --- Set up for the initial call --- */
+
+ proc = step; ctx = sctx; p = P_STEP; rq = PGEN_BEGIN;
+
+ /* --- Enter the great maelstrom of state transitions --- */
+
+ for (;;) {
+ unsigned act = 0;
+
+#define A_STEP 1u
+#define A_TEST 2u
+#define A_EVENT 4u
+#define A_ENDTEST 8u
+#define A_ENDSTEP 16u
+#define A_DONE 32u
+
+ /* --- Call the procedure and decide what to do next --- */
+
+ rc = proc(rq, &ev, ctx);
+ switch (rc) {
+ case PGEN_TRY:
+ if (p == P_TEST)
+ rq = PGEN_TRY;
+ else {
+ act |= A_EVENT;
+ proc = test; ctx = tctx; p = P_TEST;
+ rq = PGEN_BEGIN;
+ }
+ break;
+ case PGEN_PASS:
+ act |= A_TEST | A_EVENT;
+ if (p == P_TEST)
+ rq = PGEN_TRY;
+ else {
+ proc = test; ctx = tctx; p = P_TEST;
+ rq = PGEN_BEGIN;
+ }
+ break;
+ case PGEN_FAIL:
+ act |= A_STEP;
+ if (p == P_TEST) {
+ act |= A_ENDTEST | A_EVENT;
+ proc = step; ctx = sctx; p = P_STEP;
+ }
+ rq = PGEN_TRY;
+ break;
+ case PGEN_DONE:
+ act |= A_EVENT | A_DONE | A_ENDSTEP;
+ if (p == P_TEST)
+ act |= A_ENDTEST;
+ break;
+ case PGEN_ABORT:
+ act |= A_EVENT | A_DONE;
+ if (p == P_TEST || rq == PGEN_TRY)
+ act |= A_ENDSTEP;
+ if (p == P_TEST && rq != PGEN_BEGIN)
+ act |= A_ENDTEST;
+ break;
+ default:
+ assert(((void)"Invalid response from function", 0));
+ break;
+ }