//! mgf-hrb-shm — per-tenant private shared-memory region pool
//! (Part 12 §5.5, Tier-0 #5, the C6 cross-tenant side-channel closure).
//!
//! ## Where this sits in Tier-0 #5
//!
//! Tier-0 #5 has two halves:
//!
//!   * **Asymmetric trust (§3.5, U6 shm-escape)** — the broker, as ring
//!     consumer, treats every producer-written field as untrusted
//!     (mask-then-bound; never deref a client-written pointer/length).
//!     This is SHIPPED + pinned in `ring.zig` / `ring_test.zig` (the
//!     `tier0_5:` suite + `popUntrustedProducer`).
//!
//!   * **Per-tenant isolation (§5.5, C6 side-channel)** — THIS module.
//!     Each tenant gets its own PRIVATE region; no region is ever shared
//!     between two tenants, and a region is **zeroed before reuse** so no
//!     residual data crosses from one tenant to the next. This makes the
//!     cross-tenant timing/residue side channel structurally absent
//!     rather than merely mitigated.
//!
//! ## What this is
//!
//! The region-LIFECYCLE mechanism: private-per-tenant allocation,
//! zero-before-reuse, owner-checked access, and S5 bounds (aggregate +
//! per-tenant). Regions are memfd-backed (`mapRegion`) so the broker can
//! `SCM_RIGHTS`-pass the fd and both processes map the same physical pages.
//!
//! ## The live data plane (R-TIER0-5-DATAPLANE)
//!
//! The wiring is now connected to clients (slices 0–4):
//!   * `open_shm_channel` acquires a region, lays out the `HostedRing` header,
//!     and fd-passes the memfd; the SDK `ShmChannel` maps + validates it.
//!   * `drain_shm_channel` is the deterministic PULL: the broker drains the
//!     client-produced ring as the asymmetric-trust consumer and returns the
//!     count/checksum.
//!   * **slice 4 (this module's `Drainer`)** is the PUSH mode: `start_shm_drain`
//!     spawns a background per-channel thread that drains continuously, so a
//!     streaming producer needs no per-batch RPC. `drain_shm_channel` then
//!     returns that thread's accumulated stats instead of pulling.
//!
//! A connection's regions (and their drain threads) never outlive it:
//! `releaseAllForConn` runs on disconnect — keyed on the per-connection cfd, so
//! a second same-uid connection closing can't zero this one's live regions —
//! and `unmapRegion` joins the drain thread before unmapping the pages. Access
//! stays uid-owner-checked (cross-tenant isolation); only the LIFECYCLE is
//! per-connection.

const std = @import("std");
const kabi = @import("mgf_hrb_kabi");
const ring = @import("mgf_hrb_ring");
const posix = std.posix;
const PAGE = std.heap.page_size_min;

/// Backoff a background drain thread sleeps for when it finds the ring empty,
/// so an idle channel doesn't busy-spin a core. Sub-millisecond so a stop
/// signal (and the disconnect path's join) is observed promptly — the join
/// in `unmapRegion` waits at most one of these before the thread exits.
const DRAIN_BACKOFF_NS: u64 = 250 * std.time.ns_per_us;

/// Sleep `ns` nanoseconds via the raw linux syscall — std's `sleep` moved onto
/// the async `std.Io` interface (0.16), which this bare worker thread has no
/// reason to carry. Same idiom the broker's other timed paths use.
fn sleepNs(ns: u64) void {
    const ts = std.os.linux.timespec{ .sec = @intCast(ns / std.time.ns_per_s), .nsec = @intCast(ns % std.time.ns_per_s) };
    _ = std.os.linux.nanosleep(&ts, null);
}

/// Background per-channel drain thread (R-TIER0-5-DATAPLANE slice 4). When a
/// channel opts into push-mode (`start_shm_drain`), the broker spawns one of
/// these bound to the channel's region: it hosts the SPSC ring CONSUMER and
/// drains client-produced items continuously, so the data plane needs no
/// per-batch `drain_shm_channel` RPC. The ring already carries the
/// producer/consumer acquire-release ordering (`popUntrustedProducer`), so the
/// thread is a plain loop over it — arm64 weak-ordering correctness lives in
/// the ring, not here.
///
/// Lifecycle is owned by the `Region` (see `unmapRegion`): the only way to free
/// a region is through `unmapRegion`, which signals `stop` and `join`s this
/// thread BEFORE it unmaps the pages — so the consumer can never read a region
/// that's being torn down. The stats are write-only from the drain thread and
/// read under the pool lock by the dispatch path (`drainerStats`).
const Drainer = struct {
    /// The drain thread's view of the region (the same mapping the pool holds;
    /// stable for the region's whole life — only `unmapRegion` invalidates it,
    /// and that joins this thread first).
    region: []u8,
    slot_count: usize,
    thread: ?std.Thread = null,
    /// Set by `stopDrainer` (under the pool lock); polled by `run`.
    stop: std.atomic.Value(bool) = .init(false),
    /// Accumulated stats — written only by `run`, read via `drainerStats`.
    drained: std.atomic.Value(u64) = .init(0),
    checksum: std.atomic.Value(u64) = .init(0),
    last_seq: std.atomic.Value(u64) = .init(0),

    /// Pop every available item, fold it into the stats, return whether any
    /// item was drained this sweep. Bounded by `slot_count` per sweep: a ring
    /// of N slots holds at most N live items, and the cap means a hostile
    /// producer forging `head` can't make the sweep spin (same bound the
    /// deterministic `drain_shm_channel` uses).
    fn sweep(self: *Drainer, hr: *ring.HostedRing(u64)) bool {
        var progressed = false;
        var i: usize = 0;
        while (i < self.slot_count) : (i += 1) {
            const item = hr.popUntrustedProducer() orelse break;
            _ = self.drained.fetchAdd(1, .monotonic);
            _ = self.checksum.fetchXor(item.payload, .monotonic);
            self.last_seq.store(item.seq, .monotonic);
            progressed = true;
        }
        return progressed;
    }

    fn run(self: *Drainer) void {
        var hr = ring.HostedRing(u64).overlay(self.region, self.slot_count);
        while (!self.stop.load(.acquire)) {
            if (!self.sweep(&hr)) sleepNs(DRAIN_BACKOFF_NS);
        }
        // Final sweep after the stop signal: items the producer pushed between
        // the last in-loop sweep and `stop` are still drained + accounted, so
        // a clean stop loses nothing already published.
        _ = self.sweep(&hr);
    }
};

/// Snapshot of a background drainer's progress (the push-mode analog of the
/// deterministic `drain_shm_channel` reply).
pub const DrainStats = struct { drained: u64, checksum: u64, last_seq: u64, dropped: u64 };

/// Stop + join + free a region's drain thread, if any. MUST run before the
/// region's pages are unmapped (it is — `unmapRegion` calls it first). The
/// drain thread never takes the pool lock, so joining while the caller holds
/// it cannot deadlock; the wait is bounded by `DRAIN_BACKOFF_NS`.
fn stopDrainer(r: *Region, alloc: std.mem.Allocator) void {
    if (r.drainer) |d| {
        d.stop.store(true, .release);
        if (d.thread) |t| t.join();
        alloc.destroy(d);
        r.drainer = null;
    }
}

/// Region size — one page. Arbitrary for the in-memory mechanism; the
/// figure the future `mmap` layer maps per channel.
pub const REGION_BYTES: usize = 4096;

/// S5 aggregate ceiling: total regions the broker will hand out at once
/// (bounds per-tenant memory in aggregate — no actor can exhaust host
/// memory via channel-open spam).
pub const MAX_REGIONS: usize = 64;

/// S5 per-tenant ceiling: regions any single tenant (uid) may hold. Kept
/// below `MAX_REGIONS` so one tenant can't starve others of the pool —
/// the same per-tenant/aggregate shape as the Tier-1 #9 lease ceilings.
pub const MAX_REGIONS_PER_UID: usize = 16;

const Region = struct {
    /// memfd handle backing this region; -1 when free. This is the fd the
    /// broker SCM_RIGHTS-passes to the owning client so both processes map
    /// the SAME physical pages.
    fd: i32 = -1,
    /// The broker's MAP_SHARED view of the memfd; null when free.
    mapping: ?[]align(PAGE) u8 = null,
    /// SO_PEERCRED uid of the owning tenant; the ACCESS owner-check key
    /// (cross-tenant isolation — a different uid can never view/drain).
    owner_uid: u32 = 0,
    /// Per-CONNECTION creator token (the accepting `cfd`, like
    /// subscription.reapPeer). The LIFECYCLE key: a region is freed when the
    /// connection that created it disconnects — NOT when any same-uid
    /// connection disconnects. Without this, a tenant's second connection
    /// (e.g. a dedicated event-stream alongside the main one) closing would
    /// `releaseAllForUid`-zero the first connection's live regions mid-use
    /// (observed as `bad magic 0x0` after open_shm_channel under load).
    creator: u64 = 0,
    in_use: bool = false,
    /// Background drain thread for this channel (slice 4), or null in the
    /// default pull-mode. Heap-allocated so its address is stable for the
    /// thread; owned here and torn down by `unmapRegion` → `stopDrainer`.
    drainer: ?*Drainer = null,
};

/// Create a memfd, size it to one region, and map it MAP_SHARED. The memfd
/// is the fd-passable handle; the mapping is the broker's own view. Returns
/// null on any syscall failure (treated as back-pressure, like a ceiling).
fn mapRegion() ?Region {
    const fd = posix.memfd_create("mgf-hrb-shm", posix.MFD.CLOEXEC) catch return null;
    // ftruncate/close aren't in std.posix on this target — use the raw
    // linux syscalls (the broker's no-allocator boot-path style).
    if (std.os.linux.errno(std.os.linux.ftruncate(fd, REGION_BYTES)) != .SUCCESS) {
        _ = std.os.linux.close(fd);
        return null;
    }
    const bytes = posix.mmap(
        null,
        REGION_BYTES,
        .{ .READ = true, .WRITE = true },
        .{ .TYPE = .SHARED },
        fd,
        0,
    ) catch {
        _ = std.os.linux.close(fd);
        return null;
    };
    return .{ .fd = fd, .mapping = bytes, .owner_uid = 0, .in_use = false };
}

/// Zero the shared pages (cross-tenant residue closure), unmap the broker's
/// view, and close the memfd, returning the region to the free state. The
/// memfd's pages survive only as long as something maps them; a client that
/// still maps its passed copy keeps them until it (or its death) unmaps —
/// no leak, and the zeroing means no residue crosses tenants here.
fn unmapRegion(r: *Region, alloc: std.mem.Allocator) void {
    // Slice 4: join any background drain thread FIRST — it reads `r.mapping`,
    // so the pages must outlive it. This is the single funnel every free path
    // (release / releaseAllForUid / deinit) goes through, which makes
    // "free a region ⇒ its drain thread is already joined" hold by
    // construction.
    stopDrainer(r, alloc);
    if (r.mapping) |m| {
        @memset(m, 0);
        posix.munmap(m);
    }
    if (r.fd >= 0) _ = std.os.linux.close(r.fd);
    r.* = .{};
}

/// A pool of private per-tenant regions. Keyed on the unforgeable
/// SO_PEERCRED uid (consistent with the #9 lease ceilings / #12 recovery
/// — the tenant identity a client can't vary at will).
pub const RegionPool = struct {
    regions: []Region,
    alloc: std.mem.Allocator,
    /// Cold-path lock: one acquire/release per channel lifetime, never on
    /// the hot data path (which is the ring inside a region).
    lock: kabi.SpinMutex = .{},

    pub fn init(alloc: std.mem.Allocator) !RegionPool {
        const regions = try alloc.alloc(Region, MAX_REGIONS);
        for (regions) |*r| r.* = .{}; // free: fd=-1, mapping=null (no syscalls yet)
        return .{ .regions = regions, .alloc = alloc };
    }

    pub fn deinit(self: *RegionPool) void {
        for (self.regions) |*r| {
            if (r.in_use) unmapRegion(r, self.alloc); // join drainer + unmap + close
        }
        self.alloc.free(self.regions);
    }

    /// Acquire a private, zeroed region for `uid`. Returns its index, or
    /// null when a ceiling is hit (S5 back-pressure — aggregate OR
    /// per-tenant). The region is private to `uid` until released and is
    /// handed out already-zeroed, so no residual data from a previous
    /// tenant is ever visible (§5.5 / C6).
    pub fn acquire(self: *RegionPool, uid: u32, creator: u64) ?usize {
        self.lock.lock();
        defer self.lock.unlock();
        var in_use_total: usize = 0;
        var in_use_uid: usize = 0;
        var free_idx: ?usize = null;
        for (self.regions, 0..) |*r, i| {
            if (r.in_use) {
                in_use_total += 1;
                if (r.owner_uid == uid) in_use_uid += 1;
            } else if (free_idx == null) {
                free_idx = i;
            }
        }
        if (in_use_total >= MAX_REGIONS) return null; // aggregate ceiling
        if (in_use_uid >= MAX_REGIONS_PER_UID) return null; // per-tenant ceiling
        const idx = free_idx orelse return null; // unreachable below the aggregate cap
        // A fresh memfd is zero-filled by ftruncate; defensively zero the
        // mapping too so a hand-out always presents clean memory regardless
        // of history (§5.5 / C6 — no residual data crosses tenants).
        var region = mapRegion() orelse return null; // syscall failure → shed
        @memset(region.mapping.?, 0);
        region.owner_uid = uid;
        region.creator = creator;
        region.in_use = true;
        self.regions[idx] = region;
        return idx;
    }

    /// Release region `idx` held by `uid`, ZEROING it before it returns
    /// to the pool — so the next tenant to acquire it sees no residual
    /// data (§5.5 "zeroed before reuse"). Refuses (false) on a wrong
    /// owner or a not-in-use / out-of-range index: a tenant can only
    /// release its own region.
    pub fn release(self: *RegionPool, idx: usize, uid: u32) bool {
        self.lock.lock();
        defer self.lock.unlock();
        if (idx >= self.regions.len) return false;
        const r = &self.regions[idx];
        if (!r.in_use or r.owner_uid != uid) return false;
        unmapRegion(r, self.alloc); // join drainer + zero + munmap + close
        return true;
    }

    /// The owning tenant's mutable view of its region. Returns null if
    /// `idx` is out of range, free, or owned by ANOTHER uid — so one
    /// tenant can never reach another's region. The slice the future
    /// mmap layer hands the client; plain memory here so the isolation
    /// contract is unit-testable on the same shape.
    pub fn view(self: *RegionPool, idx: usize, uid: u32) ?[]u8 {
        self.lock.lock();
        defer self.lock.unlock();
        if (idx >= self.regions.len) return null;
        const r = &self.regions[idx];
        if (!r.in_use or r.owner_uid != uid) return null;
        return r.mapping;
    }

    /// The owning tenant's memfd fd, for SCM_RIGHTS passing. Same
    /// owner-check as `view` — a stranger gets null and can never obtain a
    /// handle to another tenant's region. The caller fd-passes this and the
    /// transport closes the broker's dup; the client's mapping survives.
    pub fn fdFor(self: *RegionPool, idx: usize, uid: u32) ?i32 {
        self.lock.lock();
        defer self.lock.unlock();
        if (idx >= self.regions.len) return null;
        const r = &self.regions[idx];
        if (!r.in_use or r.owner_uid != uid) return null;
        return r.fd;
    }

    /// Release EVERY region held by `uid` (the tenant disconnected),
    /// zeroing each before reuse. Returns the count freed. Mirrors the
    /// lease engine's `holderDied` reclaim — a tenant's regions never
    /// outlive its connection (no leak, no cross-tenant residue).
    pub fn releaseAllForUid(self: *RegionPool, uid: u32) usize {
        self.lock.lock();
        defer self.lock.unlock();
        var n: usize = 0;
        for (self.regions) |*r| {
            if (r.in_use and r.owner_uid == uid) {
                unmapRegion(r, self.alloc); // join drainer + zero + munmap + close
                n += 1;
            }
        }
        return n;
    }

    /// Release EVERY region CREATED BY connection `creator` (that connection
    /// disconnected), zeroing each before reuse. Returns the count freed. This
    /// is the disconnect-cleanup funnel — keyed on the per-connection `creator`
    /// (the cfd, like subscription.reapPeer), NOT the uid, so one connection's
    /// disconnect can never zero a *sibling same-uid connection's* live regions
    /// (the "bad magic 0x0 after open_shm_channel under load" flake).
    pub fn releaseAllForConn(self: *RegionPool, creator: u64) usize {
        self.lock.lock();
        defer self.lock.unlock();
        var n: usize = 0;
        for (self.regions) |*r| {
            if (r.in_use and r.creator == creator) {
                unmapRegion(r, self.alloc); // join drainer + zero + munmap + close
                n += 1;
            }
        }
        return n;
    }

    /// Count of regions currently held by `uid` (introspection / tests).
    pub fn countForUid(self: *RegionPool, uid: u32) usize {
        self.lock.lock();
        defer self.lock.unlock();
        var n: usize = 0;
        for (self.regions) |*r| {
            if (r.in_use and r.owner_uid == uid) n += 1;
        }
        return n;
    }

    /// Slice 4 — start a background drain thread on region `idx` for `uid`
    /// (push-mode). Owner-checked (a stranger can't start a drainer on another
    /// tenant's channel) and idempotent-refusing (returns false if one is
    /// already running). The region's ring header MUST already be laid out
    /// (`open_shm_channel` does this). Returns false on a bad owner/index, an
    /// already-draining channel, or a thread-spawn/alloc failure (caller maps
    /// the latter to back-pressure). The thread is torn down automatically when
    /// the region is freed (release / disconnect), so there is no leak path.
    ///
    /// NOTE: spawn happens under the pool lock — consistent with this lock's
    /// "one op per channel lifetime, never on the hot path" contract (the hot
    /// path is the ring inside the region, which the thread touches lock-free).
    pub fn startDrain(self: *RegionPool, idx: usize, uid: u32) bool {
        self.lock.lock();
        defer self.lock.unlock();
        if (idx >= self.regions.len) return false;
        const r = &self.regions[idx];
        if (!r.in_use or r.owner_uid != uid) return false;
        if (r.drainer != null) return false; // already draining
        const m = r.mapping orelse return false;
        const d = self.alloc.create(Drainer) catch return false;
        d.* = .{ .region = m, .slot_count = CHANNEL_SLOTS };
        d.thread = std.Thread.spawn(.{}, Drainer.run, .{d}) catch {
            self.alloc.destroy(d);
            return false;
        };
        r.drainer = d;
        return true;
    }

    /// Slice 4 — snapshot a background drainer's progress, or null if `idx`
    /// isn't a live push-mode channel owned by `uid`. `dropped` is read live
    /// from the ring header (the producer maintains it); the rest are the
    /// thread's accumulators. The dispatch path returns this in place of a
    /// synchronous pull when a drainer is active.
    pub fn drainerStats(self: *RegionPool, idx: usize, uid: u32) ?DrainStats {
        self.lock.lock();
        defer self.lock.unlock();
        if (idx >= self.regions.len) return null;
        const r = &self.regions[idx];
        if (!r.in_use or r.owner_uid != uid) return null;
        const d = r.drainer orelse return null;
        var hr = ring.HostedRing(u64).overlay(r.mapping.?, CHANNEL_SLOTS);
        return .{
            .drained = d.drained.load(.acquire),
            .checksum = d.checksum.load(.acquire),
            .last_seq = d.last_seq.load(.acquire),
            .dropped = hr.droppedCount(),
        };
    }
};

/// Slots per data-plane ring (power of two). With REGION_BYTES=4096 and a
/// 16-byte u64 slot, 128 slots use 2 KiB after the 256-byte header — fits
/// the page with headroom. Pinned in ring.zig's HostedRing ABI tests.
pub const CHANNEL_SLOTS: usize = 128;

// ── Broker-wide pool (module global, mirroring subscription/admission) ──
// A nullable global so importing the module without a running broker (the
// unit tests above use their own RegionPool) is harmless.
var g_pool: ?RegionPool = null;

/// Install the broker-wide region pool. Call once at startup (main).
pub fn initGlobal(alloc: std.mem.Allocator) !void {
    g_pool = try RegionPool.init(alloc);
}

/// The broker-wide pool. Only reached from the dispatch path, which runs
/// long after `initGlobal` at startup.
pub fn globalPool() *RegionPool {
    return &(g_pool.?);
}

// ===========================================================================
// Tier-0 #5 §5.5 — per-tenant isolation contract pins.
// ===========================================================================

const testing = std.testing;

test "tier0_5 §5.5: two tenants get distinct, mutually-inaccessible regions" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const bob: u32 = 1001;

    const ia = pool.acquire(alice, alice) orelse return error.NoRegion;
    const ib = pool.acquire(bob, bob) orelse return error.NoRegion;
    try testing.expect(ia != ib); // never the same physical region

    // Alice can view her own region; Bob cannot (and vice-versa).
    try testing.expect(pool.view(ia, alice) != null);
    try testing.expect(pool.view(ia, bob) == null); // cross-tenant access refused
    try testing.expect(pool.view(ib, bob) != null);
    try testing.expect(pool.view(ib, alice) == null);
}

test "tier0_5 §5.5: a region is zeroed before reuse — no cross-tenant residue" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const bob: u32 = 1001;

    const ia = pool.acquire(alice, alice) orelse return error.NoRegion;
    // Alice scribbles secrets into her region.
    const a_view = pool.view(ia, alice) orelse return error.NoView;
    @memset(a_view, 0xAB);
    // Alice disconnects.
    try testing.expect(pool.release(ia, alice));

    // Bob acquires — whatever region he gets is all-zero (no Alice residue).
    const ib = pool.acquire(bob, bob) orelse return error.NoRegion;
    const b_view = pool.view(ib, bob) orelse return error.NoView;
    for (b_view) |byte| try testing.expectEqual(@as(u8, 0), byte);
}

test "tier0_5 §5.5: aggregate ceiling sheds with null (S5)" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    // Distinct uids so the per-tenant ceiling never bites — exercise the
    // aggregate bound alone.
    var i: u32 = 0;
    while (i < MAX_REGIONS) : (i += 1) {
        try testing.expect(pool.acquire(i, i) != null);
    }
    // Pool full — the next acquire (any uid) is shed.
    try testing.expect(pool.acquire(99999, 99999) == null);
}

test "tier0_5 §5.5: per-tenant ceiling bounds one tenant without starving others" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    var i: usize = 0;
    while (i < MAX_REGIONS_PER_UID) : (i += 1) {
        try testing.expect(pool.acquire(alice, alice) != null);
    }
    // Alice is at her per-tenant ceiling.
    try testing.expect(pool.acquire(alice, alice) == null);
    // A different tenant is unaffected (per-tenant, not aggregate; the
    // per-uid cap is below the aggregate cap).
    try testing.expect(pool.acquire(1001, 1001) != null);
}

test "tier0_5 §5.5: release/view are owner-checked" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const ia = pool.acquire(alice, alice) orelse return error.NoRegion;
    // Another tenant can neither view nor release Alice's region.
    try testing.expect(pool.view(ia, 1001) == null);
    try testing.expect(!pool.release(ia, 1001));
    // The owner can.
    try testing.expect(pool.release(ia, alice));
    // Double-release is a safe no-op (already free).
    try testing.expect(!pool.release(ia, alice));
}

test "tier0_5 §5.5: releaseAllForUid frees a disconnected tenant's regions" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const bob: u32 = 1001;
    _ = pool.acquire(alice, alice);
    _ = pool.acquire(alice, alice);
    _ = pool.acquire(alice, alice);
    _ = pool.acquire(bob, bob);
    try testing.expectEqual(@as(usize, 3), pool.countForUid(alice));

    // Alice disconnects — all her regions are reclaimed (and zeroed).
    try testing.expectEqual(@as(usize, 3), pool.releaseAllForUid(alice));
    try testing.expectEqual(@as(usize, 0), pool.countForUid(alice));
    // Bob's region is untouched.
    try testing.expectEqual(@as(usize, 1), pool.countForUid(bob));
}

test "tier0_5: releaseAllForConn frees only the disconnecting connection's regions" {
    // The flake closure: two connections from the SAME uid (a main + a
    // dedicated event-stream, say). One disconnecting must NOT zero the
    // other's live regions — only releaseAllForUid did that, racing a
    // concurrent open and surfacing as "bad magic 0x0" client-side.
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const uid: u32 = 1000;
    const conn_main: u64 = 7; // cfd of the main connection
    const conn_evt: u64 = 8; // cfd of the same-uid event-stream connection
    const im = pool.acquire(uid, conn_main) orelse return error.NoRegion;
    _ = pool.acquire(uid, conn_evt) orelse return error.NoRegion;
    try testing.expectEqual(@as(usize, 2), pool.countForUid(uid));

    // The event-stream connection disconnects — only ITS region is reclaimed.
    try testing.expectEqual(@as(usize, 1), pool.releaseAllForConn(conn_evt));
    // The main connection's region is still live + owner-viewable (not zeroed).
    try testing.expectEqual(@as(usize, 1), pool.countForUid(uid));
    try testing.expect(pool.view(im, uid) != null);

    // Now the main connection disconnects — its region goes too.
    try testing.expectEqual(@as(usize, 1), pool.releaseAllForConn(conn_main));
    try testing.expectEqual(@as(usize, 0), pool.countForUid(uid));
}

test "tier0_5: fdFor is owner-checked" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const ia = pool.acquire(alice, alice) orelse return error.NoRegion;
    // The owner gets a real fd; a stranger gets null (no cross-tenant handle).
    const fd = pool.fdFor(ia, alice) orelse return error.NoFd;
    try testing.expect(fd >= 0);
    try testing.expect(pool.fdFor(ia, 1001) == null);
    // Out-of-range / free indices are null too.
    try testing.expect(pool.fdFor(9999, alice) == null);
}

test "tier0_5: a region's memfd is REAL shared memory — a second mapping sees writes" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const ia = pool.acquire(alice, alice) orelse return error.NoRegion;
    const fd = pool.fdFor(ia, alice) orelse return error.NoFd;

    // Write a pattern through the pool's own view.
    const v = pool.view(ia, alice) orelse return error.NoView;
    v[0] = 0x42;
    v[100] = 0x99;

    // Independently mmap the SAME fd (what the client does after the fd is
    // passed) — it must see the same bytes, proving genuine shared memory
    // rather than a private copy.
    const second = try posix.mmap(
        null,
        REGION_BYTES,
        .{ .READ = true },
        .{ .TYPE = .SHARED },
        fd,
        0,
    );
    defer posix.munmap(second);
    try testing.expectEqual(@as(u8, 0x42), second[0]);
    try testing.expectEqual(@as(u8, 0x99), second[100]);
}

// ===========================================================================
// Slice 4 — the background per-channel drain thread (R-TIER0-5-DATAPLANE).
// ===========================================================================

test "slice 4: background drainer accumulates the producer's pushes" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const idx = pool.acquire(alice, alice) orelse return error.NoRegion;
    const view = pool.view(idx, alice) orelse return error.NoView;
    // Lay out the ring header in the region — `open_shm_channel` does this on
    // the live path before the client maps it.
    _ = ring.HostedRing(u64).layout(view, CHANNEL_SLOTS);

    // Start the background drain thread (push-mode). Idempotent-refusing +
    // owner-checked.
    try testing.expect(pool.startDrain(idx, alice));
    try testing.expect(!pool.startDrain(idx, alice)); // already draining
    try testing.expect(pool.drainerStats(idx, 1001) == null); // stranger refused

    // Producer side (this thread) pushes a known sequence into the SAME ring.
    // k < CHANNEL_SLOTS so nothing is dropped regardless of drain timing —
    // every push lands, the consumer drains them as they arrive.
    var prod = ring.HostedRing(u64).overlay(view, CHANNEL_SLOTS);
    const k: u64 = 40;
    var expect_xor: u64 = 0;
    var n: u64 = 0;
    while (n < k) : (n += 1) {
        const payload = n *% 0x1111 +% 7;
        try testing.expect(prod.push(payload, .drop_newest));
        expect_xor ^= payload;
    }

    // Spin (bounded) until the background thread has drained all k. The drain
    // is concurrent, so poll the accumulated stats rather than assume timing.
    var stats: DrainStats = undefined;
    var spins: usize = 0;
    while (spins < 2000) : (spins += 1) {
        stats = pool.drainerStats(idx, alice) orelse return error.NoDrainer;
        if (stats.drained == k) break;
        sleepNs(std.time.ns_per_ms);
    }
    try testing.expectEqual(k, stats.drained);
    try testing.expectEqual(expect_xor, stats.checksum);
    try testing.expectEqual(k - 1, stats.last_seq); // seq == head at push time
    try testing.expectEqual(@as(u64, 0), stats.dropped);

    // Release joins the drain thread before unmapping — the test reaching here
    // (and testing.allocator finding no leaked Drainer) proves a clean stop.
    try testing.expect(pool.release(idx, alice));
}

test "slice 4: disconnect joins a running drainer (no hang, no leak)" {
    var pool = try RegionPool.init(testing.allocator);
    defer pool.deinit();
    const alice: u32 = 1000;
    const idx = pool.acquire(alice, alice) orelse return error.NoRegion;
    const view = pool.view(idx, alice) orelse return error.NoView;
    _ = ring.HostedRing(u64).layout(view, CHANNEL_SLOTS);
    try testing.expect(pool.startDrain(idx, alice));

    // The disconnect funnel: releaseAllForUid must stop+join the drain thread
    // before unmapping the region. The test returning proves no hang;
    // testing.allocator proves the Drainer was freed (no leak).
    try testing.expectEqual(@as(usize, 1), pool.releaseAllForUid(alice));
    try testing.expectEqual(@as(usize, 0), pool.countForUid(alice));
    try testing.expect(pool.drainerStats(idx, alice) == null); // channel gone
}
