""" Concurrency / race condition tests. Verifies Zinc SHM IPC is safe under concurrent reader/writer access. Uses threading to simulate real multi-process patterns. """ import threading import time import pytest from malkhut.ipc.zinc_plane import MalkhutZincPlane from malkhut.ipc.control_plane import MalkhutControlPlane, ControlPlaneFrame class TestZincSHMConcurrency: def test_writer_reader_concurrent(self): """Writer and reader operate concurrently without corruption.""" plane = MalkhutZincPlane(prefix="concurrent_test") errors = [] def writer(): try: for i in range(20): plane.publish_book({"seq": i, "ts": time.time_ns()}) time.sleep(0.001) except Exception as e: errors.append(("writer", e)) def reader(): try: for _ in range(20): try: data, seq = plane.read_book(timeout_ms=50) except Exception: pass # timeout is acceptable time.sleep(0.002) except Exception as e: errors.append(("reader", e)) t1 = threading.Thread(target=writer) t2 = threading.Thread(target=reader) t1.start() t2.start() t1.join(timeout=5) t2.join(timeout=5) plane.close_all() assert len(errors) == 0, f"Errors: {errors}" def test_multiple_readers(self): """Multiple readers can read from the same region without conflict.""" plane = MalkhutZincPlane(prefix="multi_reader_test") plane.publish_book({"test": "data"}) results = [] errors = [] def reader(idx): try: for _ in range(10): try: data, seq = plane.read_book(timeout_ms=50) results.append((idx, seq)) except Exception: pass time.sleep(0.001) except Exception as e: errors.append((idx, e)) threads = [threading.Thread(target=reader, args=(i,)) for i in range(3)] for t in threads: t.start() for t in threads: t.join(timeout=5) plane.close_all() assert len(errors) == 0 assert len(results) > 0 def test_rapid_write_read_cycles(self): """Rapid write/read cycles don't corrupt the region.""" plane = MalkhutZincPlane(prefix="rapid_test") for i in range(100): plane.publish_book({"cycle": i}) data, seq = plane.read_book(timeout_ms=50) assert data["cycle"] == i plane.close_all() class TestControlPlaneConcurrency: def test_command_write_read(self): """Control plane commands can be written and read.""" cp = MalkhutControlPlane() frame = ControlPlaneFrame( command="START", ts_ns=time.time_ns(), target_symbols=("BTCUSDT",), source="test", ) cp.publish_command(frame) cmd = cp.read_command(timeout_ms=50) assert cmd is not None assert cmd.command == "START" assert cmd.target_symbols == ("BTCUSDT",) cp.close() def test_ack_write_read(self): """ACK frames can be written and read.""" cp = MalkhutControlPlane() cp.publish_ack("START", time.time_ns(), "ok", "engine started") cmd = cp.read_command(timeout_ms=50) assert cmd is not None assert "ACK_START" in cmd.command cp.close() def test_emergency_stop(self): """Emergency stop command is processed.""" cp = MalkhutControlPlane() frame = ControlPlaneFrame( command="EMERGENCY_STOP", ts_ns=time.time_ns(), source="test", ) cp.publish_command(frame) cmd = cp.read_command(timeout_ms=50) assert cmd is not None assert cmd.command == "EMERGENCY_STOP" cp.close() class TestZincRegionIsolation: def test_different_prefixes_independent(self): """Different prefixes create independent regions.""" p1 = MalkhutZincPlane(prefix="iso_a") p2 = MalkhutZincPlane(prefix="iso_b") p1.publish_book({"source": "a"}) p2.publish_book({"source": "b"}) d1, _ = p1.read_book() d2, _ = p2.read_book() assert d1["source"] == "a" assert d2["source"] == "b" p1.close_all() p2.close_all() def test_multiple_region_types(self): """Different region types (book, account, fulfilment, risk) are independent.""" plane = MalkhutZincPlane(prefix="multi_region") plane.publish_book({"type": "book"}) plane.publish_account({"type": "account"}) plane.publish_fulfilment({"type": "fulfilment"}) plane.publish_risk({"type": "risk"}) b, _ = plane.read_book() a, _ = plane.read_account() f, _ = plane.read_fulfilment() r, _ = plane.read_risk() assert b["type"] == "book" assert a["type"] == "account" assert f["type"] == "fulfilment" assert r["type"] == "risk" plane.close_all()