skip to content
==> tests/lease.c <==
1/* The Rust's lease tests (mesh-host lease.rs), one for one: lanes sharing a ring and halting, a
2 * gated lane waiting for the blink, the bounded lockstep exchange. */
3#include "mesh/abi/base.h"
4#include "mesh/abi/control_block.h"
5#include "mesh/abi/exchange.h"
6#include "mesh/abi/fault.h"
7#include "mesh/abi/header.h"
8#include "mesh/abi/node.h"
9#include "mesh/abi/ring.h"
10#include "mesh/check.h"
11#include "mesh/lease.h"
12
13#include <pthread.h>
14#include <sched.h>
15#include <stdlib.h>
16#include <time.h>
17
18/* Squares the arg on node `first`; faults on op 9. */
19static size_t sq_max(void *self) {
20    (void)self;
21    return 4;
22}
23static bool sq_run(void *self, mesh_lease *l, const mesh_taken *taken, size_t n) {
24    (void)self;
25    for (size_t i = 0; i < n; i++) {
26        const mesh_taken *k = &taken[i];
27        if (k->op == 9) {
28            mesh_lease_fault(l, MESH_FAULT_TRAP | 7u << 8);
29            return false;
30        }
31        uint64_t t0 = mesh_host_ns();
32        mesh_lease_complete(l, l->first, k, k->arg * k->arg, t0, mesh_host_ns());
33    }
34    return true;
35}
36static const mesh_runner SQ = {sq_max, sq_run, NULL};
37
38typedef struct {
39    mesh_lease lease;
40    pthread_t th;
41    _Atomic bool done;
42    mesh_lane_stats stats;
43} lane;
44
45static void *lane_main(void *p) {
46    lane *x = p;
47    mesh_lease_run(&x->lease, &SQ, NULL);
48    return NULL;
49}
50
51static void publish(mesh_mailbox mb, uint32_t slot, uint32_t t, uint32_t op, uint32_t arg) {
52    size_t e = mesh_prog_ctl(slot) + MESH_P_RING + MESH_ENTRY_BYTES * (size_t)(t % MESH_PROG_RING);
53    mesh_mailbox_set(mb, e + MESH_E_OP, op);
54    mesh_mailbox_set(mb, e + MESH_E_ARG, arg);
55    mesh_mailbox_set(mb, e + MESH_E_SLOT, t % MESH_PROG_RESULTS);
56    mesh_mailbox_publish(mb, e + MESH_E_SEQ, t + 1);
57}
58
59static uint32_t ctl(mesh_mailbox mb, uint32_t slot, size_t off) { return mesh_mailbox_get(mb, mesh_prog_ctl(slot) + off); }
60
61static void lanes_share_a_ring_and_halt(void) {
62    mesh_mailbox mb = mesh_mailbox_test();
63    uint32_t slot = 3;
64    lane lanes[3] = {0};
65    for (uint32_t n = 0; n < 3; n++) {
66        mesh_lease_init(&lanes[n].lease, mb, slot, n, 1, false, &lanes[n].done, &lanes[n].stats);
67        pthread_create(&lanes[n].th, NULL, lane_main, &lanes[n]);
68    }
69    while (ctl(mb, slot, MESH_P_ONLINE) < 3) sched_yield();
70    /* H_BLINK does not hold ungated lanes. */
71    mesh_mailbox_set(mb, MESH_H_BLINK, 1);
72    /* At most a ring of tickets in flight (as the publisher keeps it). */
73    uint64_t t0 = mesh_host_ns();
74    for (uint32_t t = 0; t < 200; t++) {
75        size_t mark = MESH_P_MARKS + 4 * (size_t)(t % MESH_PROG_RESULTS);
76        while (t >= MESH_PROG_RING && ctl(mb, slot, mark) != t - MESH_PROG_RING + 1) {
77            CHECK(mesh_host_ns() - t0 < 5000000000ull);
78            sched_yield();
79        }
80        publish(mb, slot, t, 0, t);
81    }
82    while (ctl(mb, slot, MESH_P_DONE) < 200) {
83        if (!CHECK(mesh_host_ns() - t0 < 5000000000ull)) break;
84        sched_yield();
85    }
86    for (uint32_t t = 0; t < 200; t++) {
87        size_t c = t % MESH_PROG_RESULTS;
88        if (t + MESH_PROG_RESULTS >= 200) {
89            CHECK_U(ctl(mb, slot, MESH_P_MARKS + 4 * c), t + 1);
90            CHECK_U(ctl(mb, slot, MESH_P_RESULTS + 4 * c), t * t);
91        }
92    }
93    uint32_t tasks = 0;
94    for (uint32_t n = 0; n < 3; n++) tasks += mesh_mailbox_get(mb, mesh_node_rec(slot, n) + MESH_N_TASKS);
95    CHECK_U(tasks, 200);
96    CHECK_U(ctl(mb, slot, MESH_P_RING_HEAD), 200);
97    CHECK_U(ctl(mb, slot, MESH_P_TRACE_N), 200);
98    /* A fault: recorded once, the ticket left incomplete; then shutdown halts every lane. */
99    publish(mb, slot, 200, 9, 0);
100    while (ctl(mb, slot, MESH_P_FAULT) == 0) sched_yield();
101    CHECK_U(ctl(mb, slot, MESH_P_FAULT), MESH_FAULT_TRAP | 7u << 8);
102    mesh_mailbox_publish(mb, mesh_prog_ctl(slot) + MESH_P_SHUTDOWN, 1);
103    for (uint32_t n = 0; n < 3; n++) {
104        pthread_join(lanes[n].th, NULL);
105        CHECK(atomic_load(&lanes[n].done));
106    }
107    CHECK_U(ctl(mb, slot, MESH_P_HALTED), 3);
108    CHECK_U(ctl(mb, slot, MESH_P_DONE), 200);
109    free(mb.base);
110}
111
112static void gated_lane_waits_for_the_blink(void) {
113    mesh_mailbox mb = mesh_mailbox_test();
114    mesh_mailbox_set(mb, MESH_H_BLINK, 1);
115    lane x = {0};
116    mesh_lease_init(&x.lease, mb, 0, 0, 2, true, &x.done, &x.stats);
117    pthread_create(&x.th, NULL, lane_main, &x);
118    publish(mb, 0, 0, 0, 5);
119    struct timespec ms20 = {0, 20000000};
120    nanosleep(&ms20, NULL);
121    CHECK_U(ctl(mb, 0, MESH_P_ONLINE), 2);
122    CHECK_U(ctl(mb, 0, MESH_P_DONE), 0);
123    mesh_mailbox_set(mb, MESH_H_BLINK, 0);
124    while (ctl(mb, 0, MESH_P_DONE) < 1) sched_yield();
125    CHECK_U(ctl(mb, 0, MESH_P_RESULTS), 25);
126    mesh_mailbox_publish(mb, mesh_prog_ctl(0) + MESH_P_SHUTDOWN, 1);
127    pthread_join(x.th, NULL);
128    CHECK_U(ctl(mb, 0, MESH_P_HALTED), 2);
129    free(mb.base);
130}
131
132static void exchange_waits_bounded(void) {
133    mesh_mailbox mb = mesh_mailbox_test();
134    lane x = {0};
135    mesh_lease_init(&x.lease, mb, 0, 0, 1, false, &x.done, &x.stats);
136    CHECK_U(mesh_lease_exchange(&x.lease, 1), MESH_ROUND_TIMEOUT); /* single: no round */
137    mesh_mailbox_set(mb, MESH_H_GROUP_RANK, 1);
138    mesh_mailbox_set(mb, MESH_H_GROUP_N, 2);
139    mesh_mailbox_publish64(mb, mesh_ex_counter(0), 4);
140    CHECK_U(mesh_lease_exchange(&x.lease, 3), MESH_ROUND_OK);
141    CHECK_U(mesh_mailbox_get64(mb, mesh_ex_counter(1)), 3);
142    uint64_t t0 = mesh_host_ns();
143    CHECK_U(mesh_lease_exchange(&x.lease, 5), MESH_ROUND_TIMEOUT);
144    CHECK(mesh_host_ns() - t0 >= (uint64_t)MESH_PEER_POLLS * MESH_PEER_POLL_NS);
145    CHECK_U(atomic_load(&x.stats.rounds), 3);
146    free(mb.base);
147}
148
149int main(void) {
150    lanes_share_a_ring_and_halt();
151    gated_lane_waits_for_the_blink();
152    exchange_waits_bounded();
153    return mesh_check_done();
154}