Define the spawn-time handoff a job process consumes before any application code runs: two inherited descriptors named by SWACTOR_ARENA_FD and SWACTOR_WAKE_FD, and a fixed 48-byte little-endian header at arena offset 0 naming one control-ring region. - Canonical header layout, shared parser, and a host writer that leases the header region and control ring disjointly under the arena's own placement law, zeroes the ring, stamps its generation, and returns the handoff (env map, inheritable arena and wake descriptors, host wake eventfd) only after every write completes. - Python binding gains swactor.run(main): map the arena read-only, take ground truth from fstat, validate through the shared parser, arm FD_CLOEXEC on the wake descriptor, close the arena descriptor after mapping, and drive main on asyncio with Context.data carrying the resolved state. Fail-fast BootstrapError before main on any defect. - Rewrite jobs/tiny_linear_inference.py against the approved path API and record the slice, invariants, and remaining bridge work in the handoff doc. Verified with 12 new data-plane bootstrap guarantee tests (parser defect table, fuzzed pages, writer round-trip), 29 binding tests across in-process and exec boundaries, and full data-plane and iroh-driver suites with no regressions.
460 lines
14 KiB
Python
460 lines
14 KiB
Python
"""Bootstrap-slice behavior guarantees for the ``swactor`` Python binding.
|
|
|
|
Defends the agreed invariants:
|
|
- B1 deterministic handoff: a valid exec-time bootstrap reaches ``main``
|
|
- B2 fail-fast purity: every bootstrap defect raises ``BootstrapError``
|
|
before ``main`` is invoked
|
|
- B3 version gate rejects, never guesses
|
|
- B4 arbitrary header bytes never crash the process
|
|
- B5 grandchild isolation: post-bootstrap children inherit no bootstrap fds
|
|
- B6 wake opacity: bootstrap does not consume the wake descriptor
|
|
- B7 exit-code contract across a real process boundary
|
|
- B8 the two environment names are the whole handoff ABI
|
|
- B9 no identity leakage on the Python-visible surface
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import random
|
|
import select
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import swactor
|
|
|
|
MAGIC = int.from_bytes(b"SWBS", "little")
|
|
ENV_ARENA = "SWACTOR_ARENA_FD"
|
|
ENV_WAKE = "SWACTOR_WAKE_FD"
|
|
VERSION = 1
|
|
ARENA_SIZE = 1 << 20
|
|
|
|
MAIN_RAN = []
|
|
|
|
|
|
# ─── fixtures ────────────────────────────────────────────────────────────────
|
|
|
|
def pack_header(
|
|
arena_size=ARENA_SIZE,
|
|
ring_offset=4096,
|
|
ring_capacity=8192,
|
|
ring_generation=1,
|
|
*,
|
|
magic=MAGIC,
|
|
version=VERSION,
|
|
reserved0=0,
|
|
reserved1=0,
|
|
):
|
|
return struct.pack(
|
|
"<IHHQQQQ",
|
|
magic,
|
|
version,
|
|
reserved0,
|
|
arena_size,
|
|
ring_offset,
|
|
ring_capacity,
|
|
ring_generation,
|
|
) + struct.pack("<Q", reserved1)
|
|
|
|
|
|
def _create_backing() -> int:
|
|
if hasattr(os, "memfd_create"):
|
|
return os.memfd_create("test-arena", 0)
|
|
# Not every interpreter exposes memfd_create; an unlinked temp file is an
|
|
# equally anonymous regular-file inode for ftruncate/pwrite/mmap.
|
|
fd, path = tempfile.mkstemp(prefix="swactor-test-arena-")
|
|
os.unlink(path)
|
|
return fd
|
|
|
|
|
|
def make_arena(header: bytes, size: int = ARENA_SIZE) -> int:
|
|
fd = _create_backing()
|
|
os.ftruncate(fd, size)
|
|
os.pwrite(fd, header, 0)
|
|
return fd
|
|
|
|
|
|
def make_wake(signaled: bool) -> tuple[int, int]:
|
|
read_fd, write_fd = os.pipe()
|
|
if signaled:
|
|
os.write(write_fd, b"x")
|
|
# Keep the write end open: closing it would signal EOF and make the read
|
|
# end "readable" without a wake ever being delivered.
|
|
return read_fd, write_fd
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_env(monkeypatch):
|
|
"""A complete, valid bootstrap in the current process."""
|
|
keep_open = []
|
|
|
|
def install(*, header=None, arena_size=ARENA_SIZE, signaled=True):
|
|
arena_fd = make_arena(
|
|
header if header is not None else pack_header(arena_size=arena_size),
|
|
arena_size,
|
|
)
|
|
wake_fd, write_fd = make_wake(signaled)
|
|
keep_open.append(write_fd)
|
|
# Make the wake fd inheritable so only run()'s CLOEXEC can hide it
|
|
# from children (B5 evidence).
|
|
os.set_inheritable(wake_fd, True)
|
|
monkeypatch.setenv(ENV_ARENA, str(arena_fd))
|
|
monkeypatch.setenv(ENV_WAKE, str(wake_fd))
|
|
return arena_fd, wake_fd
|
|
|
|
yield install
|
|
for write_fd in keep_open:
|
|
os.close(write_fd)
|
|
|
|
# ─── B1 / B9: valid bootstrap reaches main with a clean surface ─────────────
|
|
|
|
def test_run_constructs_context_and_data(valid_env):
|
|
valid_env()
|
|
seen = {}
|
|
|
|
async def main(ctx):
|
|
seen["ctx"] = ctx
|
|
|
|
swactor.run(main)
|
|
assert isinstance(seen["ctx"], swactor.Context)
|
|
assert isinstance(seen["ctx"].data, swactor.DataPlane)
|
|
# B9: no identities leak onto the surface.
|
|
assert {a for a in dir(seen["ctx"]) if not a.startswith("_")} == {"data"}
|
|
leaked = {"arena", "resolved", "control_ring", "offset", "generation"}
|
|
assert not leaked & {a for a in dir(seen["ctx"].data) if not a.startswith("_")}
|
|
|
|
def test_run_passes_the_same_context_instance(valid_env):
|
|
valid_env()
|
|
seen = {}
|
|
|
|
async def main(ctx):
|
|
seen["one"] = ctx
|
|
seen["data"] = ctx.data
|
|
|
|
swactor.run(main)
|
|
assert seen["one"].data is seen["data"]
|
|
|
|
|
|
# ─── B5: fd hygiene ──────────────────────────────────────────────────────────
|
|
|
|
def test_arena_fd_closed_after_run(valid_env):
|
|
arena_fd, _ = valid_env()
|
|
|
|
async def main(ctx):
|
|
pass
|
|
|
|
swactor.run(main)
|
|
with pytest.raises(OSError):
|
|
os.fstat(arena_fd)
|
|
|
|
|
|
# ─── B6: wake opacity ────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.mark.parametrize("signaled,expected", [(True, 1), (False, 0)])
|
|
def test_bootstrap_does_not_consume_wake(valid_env, signaled, expected):
|
|
valid_env(signaled=signaled)
|
|
readable = {}
|
|
|
|
async def main(ctx):
|
|
wake = int(os.environ[ENV_WAKE])
|
|
readable["value"] = int(bool(select.select([wake], [], [], 0)[0]))
|
|
|
|
swactor.run(main)
|
|
assert readable["value"] == expected
|
|
|
|
|
|
|
|
def test_wake_fd_cloexec_armed_by_run(valid_env):
|
|
_, wake_fd = valid_env()
|
|
|
|
async def main(ctx):
|
|
child = subprocess.Popen(
|
|
[
|
|
sys.executable,
|
|
"-c",
|
|
"import os, sys; "
|
|
"sys.exit(0 if sys.argv[1] not in os.listdir('/proc/self/fd') else 3)",
|
|
str(wake_fd),
|
|
],
|
|
close_fds=False,
|
|
)
|
|
child.wait()
|
|
assert child.returncode != 3, "close_fds=False child saw the wake fd"
|
|
|
|
swactor.run(main)
|
|
|
|
|
|
# ─── B2 / B3 / B4: defect table, in process ─────────────────────────────────
|
|
|
|
def _expect_bootstrap_failure(monkeypatch, header=None, *, env=None, arena_size=ARENA_SIZE, match=""):
|
|
calls = []
|
|
|
|
async def main(ctx): # pragma: no cover - must never run
|
|
calls.append(ctx)
|
|
|
|
if env is not None:
|
|
monkeypatch.setenv(ENV_ARENA, str(env[0]))
|
|
monkeypatch.setenv(ENV_WAKE, str(env[1]))
|
|
else:
|
|
arena_fd = make_arena(header if header is not None else pack_header(arena_size=arena_size), arena_size)
|
|
wake_fd, _keep_wake = make_wake(True)
|
|
monkeypatch.setenv(ENV_ARENA, str(arena_fd))
|
|
monkeypatch.setenv(ENV_WAKE, str(wake_fd))
|
|
|
|
with pytest.raises(swactor.BootstrapError, match=match) as excinfo:
|
|
swactor.run(main)
|
|
assert isinstance(excinfo.value, swactor.SwactorError)
|
|
assert calls == [], "main must never run on a bootstrap defect"
|
|
def test_missing_env_rejected(monkeypatch):
|
|
monkeypatch.delenv(ENV_ARENA, raising=False)
|
|
monkeypatch.delenv(ENV_WAKE, raising=False)
|
|
calls = []
|
|
|
|
async def main(ctx): # pragma: no cover - must never run
|
|
calls.append(ctx)
|
|
|
|
with pytest.raises(swactor.BootstrapError, match="is not set"):
|
|
swactor.run(main)
|
|
assert calls == []
|
|
|
|
|
|
def test_non_numeric_fd_rejected(monkeypatch):
|
|
_expect_bootstrap_failure(
|
|
monkeypatch, env=("not-a-number", "also-not"), match="not a descriptor number"
|
|
)
|
|
|
|
|
|
|
|
def test_negative_fd_rejected(monkeypatch):
|
|
_expect_bootstrap_failure(monkeypatch, env=("-1", "-1"), match="non-negative")
|
|
|
|
|
|
def test_closed_fd_rejected(monkeypatch):
|
|
closed = os.pipe()[0]
|
|
os.close(closed)
|
|
_expect_bootstrap_failure(monkeypatch, env=(closed, 1), match="stat arena descriptor")
|
|
|
|
|
|
def test_short_backing_rejected(monkeypatch):
|
|
_expect_bootstrap_failure(
|
|
monkeypatch,
|
|
header=b"\0" * 16,
|
|
arena_size=16,
|
|
match="shorter than the 48-byte bootstrap header",
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"header,match",
|
|
[
|
|
(pack_header(magic=MAGIC ^ 0xFF), "magic mismatch"),
|
|
(pack_header(version=0), "unsupported bootstrap version 0"),
|
|
(pack_header(version=2), "unsupported bootstrap version 2"),
|
|
(pack_header(reserved0=1), "reserved bytes at offset 6"),
|
|
(pack_header(reserved1=1), "reserved bytes at offset 40"),
|
|
(
|
|
pack_header(arena_size=ARENA_SIZE, ring_offset=16),
|
|
"overlaps the bootstrap header",
|
|
),
|
|
(
|
|
pack_header(ring_offset=ARENA_SIZE, ring_capacity=1),
|
|
"exceeds arena size",
|
|
),
|
|
(
|
|
pack_header(ring_offset=2**64 - 8, ring_capacity=16),
|
|
"exceeds arena size",
|
|
),
|
|
(pack_header(ring_capacity=0), "capacity is zero"),
|
|
(pack_header(ring_generation=0), "generation is zero"),
|
|
],
|
|
)
|
|
def test_header_defects_rejected(monkeypatch, header, match):
|
|
_expect_bootstrap_failure(monkeypatch, header, match=match)
|
|
|
|
|
|
def test_lying_arena_size_rejected(monkeypatch):
|
|
_expect_bootstrap_failure(
|
|
monkeypatch,
|
|
pack_header(arena_size=ARENA_SIZE),
|
|
arena_size=ARENA_SIZE + 1,
|
|
match="disagrees with backing length",
|
|
)
|
|
|
|
|
|
def test_arbitrary_header_bytes_never_crash(monkeypatch):
|
|
rng = random.Random(0x53574253)
|
|
for _ in range(128):
|
|
fields = {
|
|
"arena_size": rng.choice([rng.getrandbits(64), ARENA_SIZE, 0, 1]),
|
|
"ring_offset": rng.choice(
|
|
[rng.getrandbits(64), 16, 4096, ARENA_SIZE, 2**64 - 8]
|
|
),
|
|
"ring_capacity": rng.choice([rng.getrandbits(64), 0, 1, 8192, 2**64 - 1]),
|
|
"ring_generation": rng.choice([rng.getrandbits(64), 0, 1, 7]),
|
|
}
|
|
header = pack_header(**fields)
|
|
ran = []
|
|
|
|
async def main(ctx): # pragma: no cover - only on accidental success
|
|
ran.append(ctx)
|
|
|
|
arena_fd = make_arena(header)
|
|
wake_fd, _keep_wake = make_wake(False)
|
|
monkeypatch.setenv(ENV_ARENA, str(arena_fd))
|
|
monkeypatch.setenv(ENV_WAKE, str(wake_fd))
|
|
|
|
end = fields["ring_offset"] + fields["ring_capacity"]
|
|
should_parse = (
|
|
fields["arena_size"] == ARENA_SIZE
|
|
and fields["ring_capacity"] > 0
|
|
and fields["ring_generation"] > 0
|
|
and fields["ring_offset"] >= 48
|
|
and end <= ARENA_SIZE
|
|
)
|
|
if should_parse:
|
|
swactor.run(main)
|
|
assert ran, "parser accepted fields that satisfy no invariant"
|
|
else:
|
|
with pytest.raises(swactor.BootstrapError):
|
|
swactor.run(main)
|
|
assert not ran
|
|
|
|
|
|
# ─── B7 / B1 / B8: real process boundary ────────────────────────────────────
|
|
|
|
PROBE = Path(__file__).parent / "probe.py"
|
|
|
|
|
|
def _spawn(argv, *, arena_fd, wake_fd, env_extra=None):
|
|
env = {k: v for k, v in os.environ.items() if k not in (ENV_ARENA, ENV_WAKE)}
|
|
env[ENV_ARENA] = str(arena_fd)
|
|
env[ENV_WAKE] = str(wake_fd)
|
|
if env_extra:
|
|
env.update(env_extra)
|
|
for fd in (arena_fd, wake_fd):
|
|
os.set_inheritable(fd, True)
|
|
return subprocess.run(
|
|
argv,
|
|
env=env,
|
|
pass_fds=(arena_fd, wake_fd),
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60,
|
|
)
|
|
|
|
|
|
def _parse_facts(stdout):
|
|
facts = dict(
|
|
part.split("=", 1) for part in stdout.strip().split() if "=" in part
|
|
)
|
|
return facts
|
|
|
|
|
|
def test_probe_succeeds_across_exec_boundary():
|
|
arena_fd = make_arena(pack_header())
|
|
wake_fd, _keep_wake = make_wake(signaled=True)
|
|
result = _spawn([sys.executable, str(PROBE)], arena_fd=arena_fd, wake_fd=wake_fd)
|
|
assert result.returncode == 0, result.stderr
|
|
facts = _parse_facts(result.stdout)
|
|
assert facts["HAS_DATA"] == "1"
|
|
assert facts["WAKE_READABLE"] == "1", "B6: presignaled wake must survive exec"
|
|
assert facts["WAKE_LEAKED"] == "0", "B5: grandchild must not see the wake fd"
|
|
|
|
|
|
def test_probe_reports_unsignaled_wake_across_exec_boundary():
|
|
arena_fd = make_arena(pack_header())
|
|
wake_fd, _keep_wake = make_wake(signaled=False)
|
|
result = _spawn([sys.executable, str(PROBE)], arena_fd=arena_fd, wake_fd=wake_fd)
|
|
assert result.returncode == 0, result.stderr
|
|
assert _parse_facts(result.stdout)["WAKE_READABLE"] == "0"
|
|
|
|
|
|
_MAIN_RETURN_SCRIPT = (
|
|
"import swactor\n"
|
|
"async def main(ctx):\n"
|
|
" print('MAIN_OK')\n"
|
|
"swactor.run(main)\n"
|
|
)
|
|
_MAIN_RAISE_SCRIPT = (
|
|
"import swactor\n"
|
|
"async def main(ctx):\n"
|
|
" raise RuntimeError('boom')\n"
|
|
"swactor.run(main)\n"
|
|
)
|
|
_NEVER_RUN_SCRIPT = (
|
|
"import swactor\n"
|
|
"async def main(ctx):\n"
|
|
" raise AssertionError('must not run')\n"
|
|
"swactor.run(main)\n"
|
|
)
|
|
|
|
|
|
def test_exit_zero_when_main_returns():
|
|
arena_fd = make_arena(pack_header())
|
|
wake_fd, _keep_wake = make_wake(True)
|
|
result = _spawn(
|
|
[sys.executable, "-c", _MAIN_RETURN_SCRIPT],
|
|
arena_fd=arena_fd,
|
|
wake_fd=wake_fd,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert "MAIN_OK" in result.stdout
|
|
|
|
|
|
def test_exit_nonzero_when_main_raises_with_traceback():
|
|
arena_fd = make_arena(pack_header())
|
|
wake_fd, _keep_wake = make_wake(True)
|
|
result = _spawn(
|
|
[sys.executable, "-c", _MAIN_RAISE_SCRIPT],
|
|
arena_fd=arena_fd,
|
|
wake_fd=wake_fd,
|
|
)
|
|
assert result.returncode != 0
|
|
assert "boom" in result.stderr
|
|
|
|
|
|
def test_exit_nonzero_on_bootstrap_defect_without_entering_main():
|
|
arena_fd = make_arena(pack_header(magic=MAGIC ^ 0xFF))
|
|
wake_fd, _keep_wake = make_wake(True)
|
|
result = _spawn(
|
|
[sys.executable, "-c", _NEVER_RUN_SCRIPT],
|
|
arena_fd=arena_fd,
|
|
wake_fd=wake_fd,
|
|
)
|
|
assert result.returncode != 0
|
|
assert "BootstrapError" in result.stderr
|
|
assert "must not run" not in result.stderr
|
|
|
|
|
|
def test_handoff_uses_only_the_two_env_names():
|
|
arena_fd = make_arena(pack_header())
|
|
wake_fd, _keep_wake = make_wake(True)
|
|
result = _spawn(
|
|
[sys.executable, "-c", _MAIN_RETURN_SCRIPT],
|
|
arena_fd=arena_fd,
|
|
wake_fd=wake_fd,
|
|
)
|
|
assert result.returncode == 0
|
|
# B8 is structural: the binding reads exactly ENV_ARENA and ENV_WAKE.
|
|
# Prove it by renaming one and observing failure.
|
|
env = {k: v for k, v in os.environ.items() if k not in (ENV_ARENA, ENV_WAKE)}
|
|
env["SWACTOR_DATA_PLANE_INPUT"] = "ignored-legacy-name"
|
|
env[ENV_WAKE] = str(wake_fd)
|
|
for fd in (arena_fd, wake_fd):
|
|
os.set_inheritable(fd, True)
|
|
missing = subprocess.run(
|
|
[sys.executable, "-c", _NEVER_RUN_SCRIPT],
|
|
env=env,
|
|
pass_fds=(arena_fd, wake_fd),
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60,
|
|
)
|
|
assert missing.returncode != 0
|
|
assert ENV_ARENA in missing.stderr
|