/* test_host_depth.c -- every message path of a node on a mesh, at every * depth of both stacks. docs/v4.0.0/MESH.md 7d.7, acceptance 7; 7c.6. * * console --1 [10] 2--2 [11] 3--2 [12] * * A node's message code runs on the stacks its text is using. The first * attempt at step 6d was tested on a shallow stack with every node there, * and hung on a deep one. Here node 11 does each thing a node does with a * message -- print, SEND, SEND with no way, AWAIT answered, AWAIT ended by a * GONE, pass a message on while its own text waits for room, AWAIT broken * by a line from the console, refuse a message while its own text waits for * room, send a GONE -- with its data stack holding 20 to 32 values, and from * 20 to 33 calls deep. * * EVERY RUN ENDS ONE OF TWO WAYS, and which is checked: the path WORKS, all * of it, as it does on a shallow stack; or node 11's text ends in "Stack * overflow" or "Return stack overflow" and nothing else -- with nothing * half done: no message lost that was owed, no line swallowed. Where the * stack is shallow enough it must work. And after each, every node must * answer. "It came back" is not enough: with the nucleus's tries of the * stack taken out -- (ROOM-SERVE), (ROOM) -- this test fails (it was * mutated to see). */ #include "v4/image.h" #include "v4/fabric.h" #include "v4/message.h" #include "v4/text.h" #include #include #include #include "host_map.h" static int failures = 0, checks = 0; #define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0) static v4_text tx; static v4_node nucleus; /* one node with the nucleus in it: what every node here starts as */ static v4_cell w_idle, w_fault; static v4_fabric_node pool[3]; static v4_place places[3]; static v4_fabric f; #define QUEUES 12u /* two for each wire there may be: the console's, 10-11, 11-12, 10-12, and two wires to spare */ static v4_wire_queue queues[QUEUES]; /* ---- the console: a device on a port ---- */ #define CONSOLE_ID 1 static const v4_device console = { 0, 0, 0, 0 }; /* it takes and gives no words: only whole messages */ static unsigned hera, mid, far; /* where the three nodes are: 10, 11, 12 */ static char printed[32768]; /* what has come back since the console last sent */ static unsigned printed_len; static v4_cell printed_from; /* which node the last of it came from */ static int ended; /* how many messages of how text ended have come, */ static v4_cell ended_from, ended_how; /* and the last of them */ static unsigned ended_at; /* and how much had been printed when it came */ static int nacks; /* how many NACKs have come to the console, */ static v4_cell nack_from, nack_about; /* and the last of them: who refused, and the node the refused message was for */ /* Everything the node at the console has put on the console's wire is taken, in order. How many messages. */ static unsigned console_takes(void) { static v4_cell hd[V4_WIRE_HEADER], text[V4_MSG_MAX_CHARS / 4u]; unsigned took = 0, i; while (v4_fabric_device_take(&f, hera, 1, hd, text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) { took++; if (hd[2] == V4_MSG_OUTPUT) { for (i = 0; i < (unsigned)hd[6] && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu); printed[printed_len] = 0; printed_from = hd[1]; } else if (hd[2] == V4_MSG_DONE) { ended++; ended_from = hd[1]; ended_how = text[0]; ended_at = printed_len; } else if (hd[2] == V4_MSG_NACK) { nacks++; nack_from = hd[1]; nack_about = text[0]; } } return took; } static int is_empty(const char *s) { return s[0] == 0; } /* 1 if the node is asleep (operation 8): it executes nothing until a message comes. */ static int asleep(unsigned id) { const v4_fabric_node *x = v4_fabric_node_at(&f, id); return x && x->n.doing && x->n.do_op == 8; } static unsigned step_cap = 20000000; /* how long a line may take before it is given up on */ static unsigned stuck; /* bit k: the node at place k is known to be going round for ever, and is not waited for */ static void (*watch)(void); /* called after every step, if set */ static unsigned long steps_run; static void show_stuck(const char *text) { unsigned k, q; if (!getenv("V4_MESH_STUCK")) return; for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) { const v4_node *n = &pool[k].n; printf(" stuck after \"%.40s\": node %ld p=%ld doing=%d op=%ld how=%ld wire=%u asking=%d d=%u r=%u await=%ld keep=%ld lost=%ld refused=%ld fault=%u@%ld err=%ld\n", text, (long)n->mem[ME], (long)n->p, n->doing, (long)n->do_op, (long)n->how, n->wire, n->asking, (unsigned)n->ds.depth, (unsigned)n->rs.depth, (long)n->mem[AWAIT_FROM], (long)n->mem[KEEP], (long)n->mem[LOST], (long)n->mem[REFUSED], n->fault_kind, (long)n->fault_addr, (long)n->mem[NODE_ERROR]); for (q = 0; q < V4_PORTS; q++) if (places[k].wire[q].rx) printf(" port %u: %u cells coming, %u going\n", q, places[k].wire[q].rx->used, places[k].wire[q].tx->used); } } /* Let everything that follows from what has been put happen: until nothing can, or -- with a node known to be * stuck -- until every other node has been asleep, and the console has been sent nothing, for a while. */ static int run(void) { unsigned steps = 0, quiet = 0, k; while (steps < step_cap) { unsigned did = v4_fabric_step(&f), took = console_takes(), awake = 0, running = 0; steps++; steps_run++; if (watch) watch(); /* a step in which a node only faulted counts nothing done (exec.h): it goes on from its handler at the next */ for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k) && !pool[k].n.doing && !pool[k].n.asking && !pool[k].n.stopped && !(pool[k].n.reading && !pool[k].n.given)) running = 1; if (did == 0 && took == 0 && !running) return 1; for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k) && !(stuck & 1u << k) && !(pool[k].n.doing && (pool[k].n.do_op == 8 || pool[k].n.do_op == 9))) awake = 1; quiet = (stuck && !awake && took == 0) ? quiet + 1 : 0; if (quiet >= 400000) return 1; } return 0; } /* The console sends text to a node: the message is put on the wire to the node the console is on. */ static int put_text(v4_cell node, const char *text) { static v4_message m; if (!v4_message_text(&m, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return 0; return v4_fabric_device_put(&f, hera, 1, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE; } static void forget(void) { printed_len = 0; printed[0] = 0; printed_from = -1; ended = 0; ended_from = -1; ended_how = -1; ended_at = 0; } /* Send text to a node and let everything that follows from it happen. */ static const char *tell(v4_cell node, const char *text) { forget(); if (!put_text(node, text)) return "(not put)"; if (!run()) { show_stuck(text); return "(still running)"; } return printed; } /* A StarForth node in the fabric: born, then given the nucleus and the * registers the nucleus expects, waiting for a message. */ static unsigned starforth_node(unsigned k) { int id = v4_fabric_add(&f, &pool[k], PORT); v4_node *n = &pool[k].n; memcpy(n->mem, nucleus.mem, sizeof n->mem); v4_node_stack_regs_attach(n, DSTACK_REG, RSTACK_REG); v4_node_error_attach(n, NODE_ERROR); v4_node_fault_attach(n, w_fault); n->p = w_idle; return (unsigned)id; } /* Three nodes that know nothing, in a row, the console on the first. */ static int fresh(void) { unsigned i; int ok; v4_fabric_init(&f, places, 3); v4_fabric_queues(&f, queues, QUEUES); v4_fabric_gone_error(&f, V4_ERROR_NO_ONE); hera = starforth_node(0); mid = starforth_node(1); far = starforth_node(2); ok = v4_fabric_wire_device(&f, hera, 1, &console) && v4_fabric_wire(&f, hera, 2, mid, 2) && v4_fabric_wire(&f, mid, 3, far, 2); stuck = 0; watch = 0; nacks = 0; step_cap = 20000000; forget(); for (i = 0; i < 200; i++) (void)v4_fabric_step(&f); return ok && asleep(hera) && asleep(mid) && asleep(far); } static int told(v4_cell node, const char *text) { return is_empty(tell(node, text)) && ended == 1 && ended_how == V4_TEXT_COMPLETED; } /* The same, told who they are, the way to each other, and who is on each port: * console --1 [10] 2--2 [11] 3--2 [12] */ static int row(void) { return fresh() && told(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE 0 2 ROUTE") && told(0, "11 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE 0 3 ROUTE") && told(0, "12 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE") && told(10, "0 NO-ROUTE 11 2 ROUTE 12 2 ROUTE 11 2 NEIGHBOUR") && told(11, "0 NO-ROUTE 12 3 ROUTE 10 2 NEIGHBOUR 12 3 NEIGHBOUR") && told(12, "11 2 NEIGHBOUR"); } /* ---- the sweep ---- */ static unsigned runs, bad, worked, refused_clean; /* what the paths that wait for room watch for: node 11 asleep for room, and, while it is, a message it has * passed on to the wire to node 12 */ static unsigned saw_room_wait, saw_passed; static void watch_mid(void) { if (pool[mid].n.doing && pool[mid].n.do_op == 9) { saw_room_wait++; if (places[mid].wire[3].tx && places[mid].wire[3].tx->used > 0) saw_passed++; } } /* A MESSAGE THAT HAS BEEN PASSED ON AS OFTEN AS IT MAY BE comes to node 11 from node 12, for a node 99: its * fourth word is 1. No row of three can make one -- it takes sixteen nodes, or a ring -- so it is put on the * wire here, once, the first time node 11 is asleep for room. Node 11 refuses it without looking for its way: * the one refusal in which a message is dropped before the node's deepest use of its stacks. */ static int spent_put; static void put_spent(void) { static const v4_cell hd[V4_WIRE_HEADER] = { 99, 12, V4_MSG_TEXT, 1, 0, 0, 4 }, text[1] = { 0x31 }; if (!spent_put && places[mid].wire[3].rx) spent_put = v4_wire_put(places[mid].wire[3].rx, hd, text) == V4_WIRE_DONE; } static void watch_mid_spent(void) { watch_mid(); if (saw_room_wait) put_spent(); } /* words that push 1, 2, 4, 8 and 16 values and call nothing while they do */ static void fill(char *text, size_t cap, unsigned count) { size_t at = 0; unsigned bit; text[0] = 0; for (; count >= 16; count -= 16) at += (size_t)snprintf(text + at, cap - at, "P16 "); for (bit = 8; bit; bit >>= 1) if (count >= bit) { at += (size_t)snprintf(text + at, cap - at, "P%u ", bit); count -= bit; } } #define PATHS 10u static const char *const path_name[PATHS] = { "a text that prints", "SEND", "SEND with no way", "AWAIT answered", "AWAIT ended by a GONE", "passing on while its text waits for room", "AWAIT broken by a line from the console", "a refusal while its text waits for room", "GONE", "a spent message refused while its text waits" }; static char numbers[8192]; /* what 1200 0 DO I . LOOP prints */ /* A row made afresh for one path; node 11 is given the word ACT that takes it, and the words that fill the * stack and that call each other. */ static int made(unsigned path) { static const char *const act[PATHS] = { ": ACT 600 0 DO 65 EMIT LOOP ;", ": ACT S\" 1 DROP\" 12 SEND ;", ": ACT S\" 1 DROP\" 99 SEND ;", ": ACT S\" 1 DROP\" 12 SEND 12 AWAIT DROP ;", ": ACT 12 AWAIT DROP ;", ": ACT 1200 0 DO I . LOOP ;", ": ACT 12 AWAIT DROP ;", ": ACT 1200 0 DO I . LOOP ;", ": ACT 44 12 GONE ;", ": ACT 1200 0 DO I . LOOP ;", }; char line[64]; unsigned k; if (!row() || !told(11, act[path]) || !told(11, ": P1 7 ; : P2 7 7 ; : P4 7 7 7 7 ; : P8 7 7 7 7 7 7 7 7 ;") || !told(11, ": P16 P8 P8 ;")) return 0; if (!told(11, ": N1 ACT ;")) return 0; for (k = 2; k <= V4_RET_DEPTH + 1u; k++) { snprintf(line, sizeof line, ": N%u N%u ;", k, k - 1u); if (!told(11, line)) return 0; } if (path == 2 && !told(11, "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 12 3 ROUTE")) return 0; /* no way for any node it has not been told of */ if (path == 4 && !told(12, ": LL 400000 0 DO LOOP ;")) return 0; if (path == 5 && !told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW S\" 1 DROP\" 12 SEND SLOW ;")) return 0; if (path == 9 && !told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;")) return 0; /* 320 characters: more than fits beside what node 11 itself is waiting to print */ if (path == 7 && !(told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;") && told(12, ": LATE 250000 0 DO LOOP PAD 320 BLANK PAD 320 99 SEND ;"))) return 0; return 1; } /* 1 if `s` is `lead`, then some of `body` from its start, then exactly `end`. */ static int is_cut_short(const char *s, const char *body, const char *end) { size_t n = strlen(s), e = strlen(end); return n >= e && strcmp(s + n - e, end) == 0 && strncmp(s, body, n - e) == 0 && n - e <= strlen(body); } /* Node 11 takes the path with `text`; `values` is how many values the text leaves on its stack if it completes; * `must` says the stack is shallow enough that it must work. Returns 1 if it worked, 0 if it ended in a clean * stack error, and counts a failure if it did neither. */ static int one(unsigned path, const char *what, unsigned depth, const char *text, unsigned values, int must) { static char sixhundred[601]; const char *why = 0, *over = 0; v4_cell lost11 = pool[mid].n.mem[LOST], refused12 = pool[far].n.mem[REFUSED], ways12; int how11, works = 0, clean_error = 0; unsigned left; v4_uheat_t clock12; if (!sixhundred[0]) memset(sixhundred, 'A', 600); runs++; step_cap = 60000000; if (path == 8 && !told(12, "44 2 ROUTE")) why = "node 12 did not take a way"; ways12 = pool[far].n.mem[ROUTE_COUNT]; forget(); saw_room_wait = 0; saw_passed = 0; clock12 = pool[far].es.anticlock; if (why) { } else if (path == 4) { /* node 12 is busy for a long while and sends no answer; node 11 waits; its centre says 12 is gone */ stuck = 1u << far; if (!put_text(12, "LL") || !put_text(11, text) || !run()) why = "it did not come to wait"; else if (!put_text(10, "12 11 GONE") || !run()) why = "the GONE was not done"; stuck = 0; if (!why && !run()) why = "the nodes did not come to rest"; } else if (path == 5) { /* node 10, by which node 11 prints, is busy, and sends node 12 a message by node 11 meanwhile */ watch = watch_mid; if (!put_text(11, text) || !put_text(10, "BUSY") || !run()) why = "the nodes did not come to rest"; watch = 0; } else if (path == 6) { /* node 12 sends no answer; node 11 waits, and is typed to */ if (!put_text(11, text) || !run()) why = "it did not come to wait"; else if (!put_text(11, "77 .") || !run()) why = "the nodes did not come to rest"; } else if (path == 7) { /* node 10 is busy; node 12 sends a message for 99 by node 11, whose way for it is the full wire */ watch = watch_mid; if (!put_text(11, text) || !put_text(10, "BUSY") || !put_text(12, "LATE") || !run()) why = "the nodes did not come to rest"; watch = 0; } else if (path == 9) { /* node 10 is busy; a spent message comes while node 11 waits for room -- or, if it never does, afterwards */ spent_put = 0; watch = watch_mid_spent; if (!put_text(11, text) || !put_text(10, "BUSY") || !run()) why = "the nodes did not come to rest"; watch = 0; if (!why && !spent_put) { put_spent(); if (!run()) why = "the nodes did not come to rest"; } } else { if (!put_text(11, text) || !run()) why = "the nodes did not come to rest"; } how11 = (int)pool[mid].n.mem[LINE_STATUS]; left = (unsigned)pool[mid].n.ds.depth; if (!why && !(asleep(hera) && asleep(mid) && asleep(far))) why = "a node is not asleep"; if (!why && ended < 1) why = "node 11's text was not answered"; /* which of the two ways did it end? */ if (!why) { if (strstr(printed, "Return stack overflow\n")) over = "Return stack overflow\n"; else if (strstr(printed, "Stack overflow\n")) over = "Stack overflow\n"; switch (path) { case 0: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, sixhundred) == 0 && left == values; clean_error = over && is_cut_short(printed, sixhundred, over); break; case 1: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[far].es.anticlock != clock12 && pool[mid].n.mem[LOST] == lost11; /* node 12 did the text */ clean_error = over && strcmp(printed, over) == 0 && pool[far].es.anticlock == clock12; break; /* nothing was sent */ case 2: works = how11 == V4_TEXT_ERROR && strcmp(printed, "Argument out of range\n") == 0 && left == values; clean_error = over && strcmp(printed, over) == 0; break; case 3: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[mid].n.mem[LOST] == lost11; clean_error = over && strcmp(printed, over) == 0; break; case 4: works = how11 == V4_TEXT_ERROR && strcmp(printed, "Node gone\n") == 0 && left == values; clean_error = over && strcmp(printed, over) == 0; break; case 5: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0 && saw_passed > 0; clean_error = over && is_cut_short(printed, numbers, over); break; case 6: works = strcmp(printed, "Interrupted\n77 ") == 0 && ended == 2; if (over) { char want[64]; snprintf(want, sizeof want, "%s77 ", over); clean_error = strcmp(printed, want) == 0 && ended == 2; } break; case 7: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0 && pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1; clean_error = over && is_cut_short(printed, numbers, over) && pool[far].n.mem[REFUSED] == refused12 + 1; break; /* whoever refused it, node 12 was told */ case 8: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[far].n.mem[ROUTE_COUNT] == ways12 - 1; clean_error = over && strcmp(printed, over) == 0; break; case 9: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0 && pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1; clean_error = over && is_cut_short(printed, numbers, over) && pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1; break; /* it was dropped: its sender was told */ } if (clean_error && !(path == 6 || (how11 == V4_TEXT_ERROR && left == 0))) clean_error = 0; /* a stack fault empties the stack */ if (works && over) works = 0; if (!works && !clean_error) why = "it neither worked nor ended in a stack error and nothing else"; else if (!works && must) why = "it ended in a stack error where the stack is shallow enough for it to work"; } /* every node comes back */ if (!why && !(strcmp(tell(11, "ABORT"), "") == 0 && ended == 1 && pool[mid].n.ds.depth == 0 && pool[mid].n.mem[AWAIT_FROM] == 0)) why = "node 11 did not answer ABORT"; if (!why && path == 4 && !told(11, "12 3 ROUTE")) why = "node 11 did not take the way to 12 again"; if (!why && strcmp(tell(11, "1 2 + ."), "3 ") != 0) why = "node 11 did not answer"; if (!why && strcmp(tell(10, "1 2 + ."), "3 ") != 0) why = "node 10 did not answer"; if (!why && strcmp(tell(12, "1 2 + ."), "3 ") != 0) why = "node 12 did not answer"; if (!why && !(asleep(hera) && asleep(mid) && asleep(far))) why = "a node is not asleep afterwards"; checks++; if (why) { failures++; bad++; printf("FAIL %s, %s %u: %s: how %d, %u left, lost %+ld, refused %+ld, waits %u/%u: \"%.50s\"\n", path_name[path], what, depth, why, how11, left, (long)(pool[mid].n.mem[LOST] - lost11), (long)(pool[far].n.mem[REFUSED] - refused12), saw_room_wait, saw_passed, printed); show_stuck(text); (void)made(path); /* the next begins on a row that is whole */ return -1; } if (works) worked++; else refused_clean++; return works; } int main(void) { unsigned path, k; char text[160]; printf("v4 mesh depth tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS); { static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4", "numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" }; CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles"); } CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx)); if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; } w_idle = v4_text_word(&tx, "(IDLE)"); w_fault = v4_text_word(&tx, "(FAULTS)"); nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */ nucleus.mem[LATEST] = v4_text_latest(&tx); nucleus.mem[CFP] = CFS_W; nucleus.mem[BASE] = 10; nucleus.mem[FENCE] = DICT_W; nucleus.mem[BOOT_CELLS] = nucleus.mem[DP]; nucleus.mem[BOOT_CELLS + 1] = nucleus.mem[LATEST]; nucleus.mem[LOG_LEVEL] = 2; nucleus.mem[CONTEXT] = LATEST; nucleus.mem[CURRENT] = LATEST; nucleus.mem[SRC] = TIB; nucleus.mem[LINE_STATUS] = 1; HOST_MESSAGES_EMPTY(&nucleus); { size_t at = 0; for (k = 0; k < 1200; k++) at += (size_t)snprintf(numbers + at, sizeof numbers - at, "%u ", k); } for (path = 0; path < PATHS; path++) { unsigned before = bad, w0 = worked, e0 = refused_clean, first_error = 0; int r; CHECK(made(path), "a row for path %u", path); if (failures > (int)bad) break; /* the data stack at every depth from 20 to full: with 24 or fewer every path must work */ for (k = V4_DATA_DEPTH - 12u; k <= V4_DATA_DEPTH; k++) { size_t at; fill(text, sizeof text, k); at = strlen(text); snprintf(text + at, sizeof text - at, "ACT"); r = one(path, "values on the stack", k, text, k, k + 8u <= V4_DATA_DEPTH); if (r == 0 && !first_error) first_error = k; } printf(" %-42s %2u..%u values: works to %u, then a stack error and nothing else;", path_name[path], (unsigned)(V4_DATA_DEPTH - 12u), (unsigned)V4_DATA_DEPTH, first_error ? first_error - 1u : (unsigned)V4_DATA_DEPTH); /* and from every depth of calls from 14 to past the end of the return stack: 21 deep or less every path * must work -- 16 or less for the two that print and are woken while they wait for room, which go * deepest: the loop, the word that prints, EMIT, the wait, and (SERVE) under it */ first_error = 0; for (k = V4_RET_DEPTH - 18u; k <= V4_RET_DEPTH + 1u; k++) { snprintf(text, sizeof text, "N%u", k); r = one(path, "calls deep", k, text, 0, k + ((path == 5 || path == 7 || path == 9) ? 16u : 11u) <= V4_RET_DEPTH); if (r == 0 && !first_error) first_error = k; } printf(" calls: works to %u deep; %u worked, %u clean errors, %u failures\n", first_error ? first_error - 1u : (unsigned)(V4_RET_DEPTH + 1u), worked - w0, refused_clean - e0, bad - before); } /* ---- between texts: node 11 passes on, refuses, and holds what it has no room to pass on, with as many * values waiting on its stack as a text may leave (MESH.md 7d.4: the rule of 28). Not one fault. ---- */ CHECK(row() && told(11, ": P1 7 ; : P2 7 7 ; : P4 7 7 7 7 ; : P8 7 7 7 7 7 7 7 7 ;") && told(11, ": P16 P8 P8 ;") && told(11, "77 5 ROUTE") && told(12, ": S77 S\" 1 DROP\" 77 SEND ; : PR 1200 0 DO I . LOOP ;") && told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;"), "a row; node 11's way to 77 is a port with no one on it"); for (k = V4_DATA_DEPTH - 12u; k + 4u <= V4_DATA_DEPTH; k++) { unsigned faults; v4_cell lost = pool[mid].n.mem[LOST], refused = pool[far].n.mem[REFUSED]; fill(text, sizeof text, k); CHECK(told(11, text) && pool[mid].n.ds.depth == k, "node 11's text leaves %u values on its stack", k); faults = pool[mid].n.faults; CHECK(strcmp(tell(12, "1 2 + ."), "3 ") == 0 && ended == 1, "with %u values waiting it passes text on to node 12, and what comes back", k); CHECK(told(12, "S77") && pool[mid].n.mem[LOST] == lost + 1 && pool[far].n.mem[REFUSED] == refused + 1, "and refuses a message it has no way for, and tells node 12"); forget(); saw_room_wait = 0; watch = watch_mid; CHECK(put_text(12, "PR") && put_text(10, "BUSY") && run() && strcmp(printed, numbers) == 0 && saw_room_wait > 0, "and holds what node 12 prints while node 10 is busy, asleep for room, and passes all of it on: %u characters", printed_len); watch = 0; CHECK(pool[mid].n.faults == faults && pool[mid].n.ds.depth == k && asleep(mid), "without a fault, its stack as it was: %u values", (unsigned)pool[mid].n.ds.depth); CHECK(strcmp(tell(11, "ABORT"), "") == 0 && strcmp(tell(11, "1 2 + ."), "3 ") == 0, "and it goes on"); } printf(" %u runs: %u worked, %u ended in a stack error and nothing else, %u did neither\n", runs, worked, refused_clean, bad); printf(" %d checks, %d failures\n", checks, failures); return failures != 0; }