writeonce/runtime/test/test_mailbox.c
shoney.arickathil dd7dd42bd1 feat: bounded mailboxes + WO_T_ACTOR (trap 13); try-arm place-copy fix
- cap 1024 (WO_MAILBOX override at boot): sender-side atomic
  reserve/release on every path — same-shard, cross-shard envelope,
  OOM rollbacks; full mailbox traps the SENDER catchably; delivery
  pop releases; overshoot bounded by in-flight sends (disclosed)
- test_mailbox 12/0: exact cap single-threaded, two racing senders win
  exactly cap slots, drain/refill clean
- corpus run/mailbox-full-trap: parked sleeper, send loop catches
  "actor mailbox full" after >= 1024 sends
- pre-existing compiler bug found + fixed: a try ARM yielding a Text
  PLACE (bare e.msg, try box.field) aliased a register the arm's scope
  end freed — ASan use-after-free, SEGV on the next unwind's
  double-walk; emit_try now applies copy_place_text to both arm
  results; pinned by corpus run/catch-msg-place
- db-bench driver: msgrate keeps iteration 22's unbounded-flood
  contract via WO_MAILBOX=MSG_N (the cap is 24's policy, not 22's)
- battery 12/12 fresh-built

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 01:05:13 +02:00

53 lines
1.6 KiB
C

/* test_mailbox — the fail-fast cap core (iteration 24). Reserve/release
* arithmetic single-threaded, then two racing senders: successful
* reserves never exceed the cap (each success must observe old < cap,
* and increments are permanent — see wo_mbox_reserve). */
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "t.h"
#include "vm.h"
static wo_actor A;
static void *hammer(void *arg) {
(void)arg;
long wins = 0;
for (int i = 0; i < 1000; i++)
if (wo_mbox_reserve(&A) == 0) wins++;
return (void *)wins;
}
int main(void) {
/* single-threaded: cap honored exactly, release frees a slot */
wo_mailbox_cap = 4;
memset(&A, 0, sizeof A);
T_EQ(wo_mbox_reserve(&A), 0);
T_EQ(wo_mbox_reserve(&A), 0);
T_EQ(wo_mbox_reserve(&A), 0);
T_EQ(wo_mbox_reserve(&A), 0);
T_EQ(wo_mbox_reserve(&A), -1); /* full */
wo_mbox_release(&A);
T_EQ(wo_mbox_reserve(&A), 0); /* freed slot reusable */
T_EQ(wo_mbox_reserve(&A), -1);
T_EQ(A.pending, 4);
/* two racing senders against cap 8: exactly 8 wins, pending == 8 */
wo_mailbox_cap = 8;
memset(&A, 0, sizeof A);
pthread_t t1, t2;
void *w1, *w2;
pthread_create(&t1, NULL, hammer, NULL);
pthread_create(&t2, NULL, hammer, NULL);
pthread_join(t1, &w1);
pthread_join(t2, &w2);
T_EQ((long)w1 + (long)w2, 8);
T_EQ(A.pending, 8);
/* drain and refill: the counter did not corrupt under the race */
for (int i = 0; i < 8; i++) wo_mbox_release(&A);
T_EQ(A.pending, 0);
T_EQ(wo_mbox_reserve(&A), 0);
return t_report("test_mailbox");
}