diff --git a/v4/include/v4/fabric.h b/v4/include/v4/fabric.h index 9f754c79..12ae10c5 100644 --- a/v4/include/v4/fabric.h +++ b/v4/include/v4/fabric.h @@ -148,7 +148,7 @@ void v4_fabric_wake(v4_fabric *f, unsigned id); * Sleep (8) and sleep for room (9) are the two that wait: the node stays * blocked, executing nothing, until a message has come to one of its wires * since it last fetched which wires have one -- or, for 9, until its wire - * has the room. A message has "come" when a put or a move or a device put + * has the room (a wire that can never have the room answers 1 at once). A message has "come" when a put or a move or a device put * put it on the back of a queue; fetching Have clears that for all of the * node's wires. * diff --git a/v4/include/v4/wire.h b/v4/include/v4/wire.h index 910f7f7c..859d0b62 100644 --- a/v4/include/v4/wire.h +++ b/v4/include/v4/wire.h @@ -30,6 +30,9 @@ #define V4_WIRE_HEADER 7u /* the words of a message that are not its text */ +/* a wire must be able to hold the largest message there is: 1,024 characters */ +typedef char v4_wire_holds_a_message[(V4_WIRE_CELLS >= 7 + 1024 / 4) ? 1 : -1]; + typedef struct { v4_cell cell[V4_WIRE_CELLS]; /* the messages, one after another, going round */ unsigned head; /* where the front message begins */ diff --git a/v4/src/fabric.c b/v4/src/fabric.c index 13f2028c..0c167ad3 100644 --- a/v4/src/fabric.c +++ b/v4/src/fabric.c @@ -370,8 +370,8 @@ static int operate(v4_fabric *f, unsigned id, v4_cell *how) break; case 9: /* sleep for room */ if (!tx) { *how = V4_WIRE_NO_ONE; break; } - if (a < 0) { *how = V4_WIRE_NOT_A_MESSAGE; break; } - if (a <= (v4_cell)V4_WIRE_CELLS && v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; } + if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { *how = V4_WIRE_NO_ROOM; break; } /* no waiting can make that room */ + if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; } if (!arrived(f, id)) return 0; *how = V4_WIRE_NO_ROOM; break; diff --git a/v4/src/wire.c b/v4/src/wire.c index 41c38d0a..d524aa1d 100644 --- a/v4/src/wire.c +++ b/v4/src/wire.c @@ -118,7 +118,7 @@ int v4_wire_move(v4_wire_queue *from, v4_wire_queue *to) if (from == to || v4_wire_room(to, n) != V4_WIRE_DONE) return V4_WIRE_NO_ROOM; at = back(to, n); for (i = 0; i < n; i++) to->cell[slot(to, at + i)] = from->cell[slot(from, from->mark + i)]; - to->cell[slot(to, at + 3u)] = (v4_cell)(from->cell[slot(from, from->mark + 3u)] - 1); + to->cell[slot(to, at + 3u)] = (v4_cell)((v4_ucell)from->cell[slot(from, from->mark + 3u)] - 1u); /* in the unsigned type: no overflow at the least cell */ remove_marked(from); return V4_WIRE_DONE; } diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index d0f61b6f..f59db470 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -684,6 +684,23 @@ int main(void) (void)v4_fabric_step(&f); CHECK(!ND(a)->n.doing && ND(a)->n.how == 0, "and wakes it"); + /* a message put in the same engine step as the node's fetch of "which wires" is not lost: the next sleep does not block */ + v4_fabric_init(&f, places, 4); + v4_fabric_queues(&f, qpool, 8); + a = (unsigned)v4_fabric_add(&f, &pool[0], PB); + b = (unsigned)v4_fabric_add(&f, &pool[1], PB); + CHECK(v4_fabric_wire(&f, a, 0, b, 3), "two nodes for the same-step put"); + (void)v4_node_fetch(&ND(a)->n, P_HAVE); /* the fetch is seen in the step's execute phase ... */ + put_msg(&ND(b)->n, 100, 110, 3, 1); + v4_node_store(&ND(b)->n, P_WIRE, 3); + v4_node_store(&ND(b)->n, P_A, 100); + v4_node_store(&ND(b)->n, P_B, 110); + v4_node_store(&ND(b)->n, P_DO, 5); /* ... and the put is done in the same step's operations */ + (void)v4_fabric_step(&f); + CHECK(!ND(b)->n.doing && ND(b)->n.how == 0 && f.place[a].wire[0].rx->used == 8 && f.place[a].wire[0].rx->arrived != 0, + "the put and the fetch were in one step: the message is on the wire and counts as arrived"); + CHECK(ask(a, 0, 0, 0, 8) == 0, "so the sleep that follows does not block"); + /* sleep for room */ v4_fabric_init(&f, places, 4); v4_fabric_queues(&f, qpool, 8); @@ -695,18 +712,40 @@ int main(void) "room, with 100 cells free: for 50 and for 100, not for 101"); CHECK(ask(a, 5, 10, 0, 10) == 2 && ask(a, 0, -1, 0, 10) == 4, "room on a port with nothing is 2, for -1 cells 4"); CHECK(f.place[a].wire[0].tx->used == V4_WIRE_CELLS - 100 && !ND(a)->n.doing, "and it changes nothing"); + CHECK(ask(a, 0, V4_WIRE_CELLS + 1, 0, 9) == 1 && ask(a, 0, 100000, 0, 9) == 1 && ask(a, 0, -1, 0, 9) == 1 && !ND(a)->n.doing, + "sleep for more room than the wire can ever have answers 1 at once, not blocked"); (void)v4_node_fetch(&ND(a)->n, P_HAVE); (void)v4_fabric_step(&f); CHECK(ask(a, 0, 200, 0, 9) == -99 && ND(a)->n.doing, "sleep for 200 cells with 100 free: blocked"); for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); CHECK(ND(a)->n.doing, "and still blocked"); v4_wire_first(f.place[b].wire[3].rx); - CHECK(ask(b, 3, 400, 420, 4) == 0, "the reader drops one message (which takes a few cells)"); - CHECK(ND(a)->n.doing || f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken when the reader has taken enough, not before"); + CHECK(ask(b, 3, 400, 420, 4) == 0, "the reader drops the front message (which is 263 cells)"); while (ND(a)->n.doing && f.place[b].wire[3].rx->used > 0) { (void)ask(b, 3, 400, 420, 4); } CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken, with how it went 0"); + /* a small message in front: dropping it frees little, and the sleeper wants more than that leaves */ + v4_wire_reset(f.place[a].wire[0].tx); + v4_wire_reset(f.place[b].wire[3].rx); + { + v4_cell hh[7] = { 1, 2, 3, 4, 5, 6, 12 }; + static v4_cell tt[V4_WIRE_CELLS]; + (void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 10 cells */ + hh[6] = 1024; + for (i = 0; i < 3; i++) (void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); + hh[6] = 4 * (35 - 7); + (void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 35 cells: 190 are free */ + } + CHECK(V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 190, "a wire with 190 free and a small message in front"); + (void)v4_node_fetch(&ND(a)->n, P_HAVE); + (void)v4_fabric_step(&f); + CHECK(ask(a, 0, 250, 0, 9) == -99, "asleep for 250"); + v4_wire_first(f.place[b].wire[3].rx); + CHECK(ask(b, 3, 400, 420, 4) == 0 && V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 200, "the reader drops the small one: 200 are free"); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.doing, "and the sleeper, which wants 250, is not woken by a take that did not give it room"); + CHECK(ask(b, 3, 400, 420, 4) == 0 && !ND(a)->n.doing && ND(a)->n.how == 0, "the next take does, with 0"); /* woken by a message coming instead */ v4_wire_reset(f.place[a].wire[0].tx); fill(f.place[a].wire[0].tx, 100); diff --git a/v4/tests/test_wire.c b/v4/tests/test_wire.c index 46918da7..58547fc4 100644 --- a/v4/tests/test_wire.c +++ b/v4/tests/test_wire.c @@ -235,6 +235,26 @@ int main(void) for (n = 0; n < 9 && t[n] == wt[n]; n++) { } CHECK(n == 9, "and every word of its text"); } + /* the fourth word is one fewer for every value, the most negative cell included (no overflow) */ + { + const v4_cell least = (v4_cell)((v4_ucell)1 << (V4_CELL_BITS - 1)); + const v4_cell most = (v4_cell)(((v4_ucell)1 << (V4_CELL_BITS - 1)) - 1u); + static const int which[3] = { 0, 1, 2 }; + unsigned m; + for (m = 0; m < 3; m++) { + v4_wire_reset(&q); v4_wire_reset(&other); + make(h, t, 8, 5); + h[3] = which[m] == 0 ? least : which[m] == 1 ? 0 : most; + CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "(a message whose fourth word is an extreme)"); + v4_wire_first(&q); + CHECK(v4_wire_move(&q, &other) == V4_WIRE_DONE, "moved"); + v4_wire_first(&other); + CHECK(v4_wire_take(&other, h2, t2, V4_WIRE_CELLS) == V4_WIRE_DONE, "and taken"); + CHECK(h2[3] == (which[m] == 0 ? most : which[m] == 1 ? (v4_cell)-1 : (v4_cell)(most - 1)), + "its fourth word is one fewer, wrapping at the least cell: %ld", (long)h2[3]); + } + } + /* no room in the other: 1, and neither changes */ v4_wire_reset(&q); v4_wire_reset(&other); ok = 1;