A subsecond hot-patch guinea pig: no game, no window, just a loop that
calls one function through subsecond::call once a second and watches
for a patch to land. See ../README.md for the file it watches and what
a successful run looks like.
The counter tick prints and increments itself. A plain static, not a
parameter threaded through a closure - see ../CLAUDE.md for why
subsecond::call needs a bare fn item passed directly
(subsecond::call(tick)), never || tick(n).
15static TICKS: AtomicU64 = AtomicU64::new(0);
The function tools/hotpatch patches. The string it prints is the only
thing that changes between v1/v2/v3 - the loop, the pid, and the process
itself never restart. #[inline(never)] is load-bearing, not tidiness:
see ../CLAUDE.md.
27fn main() { 28 // A patcher needs both of these before it can build anything: the pid to 29 // address the running process's MCP-equivalent (here, just a log line a 30 // human/agent reads), and the ASLR reference `subsecond::apply_patch` 31 // rebases the jump table against (subsecond/src/lib.rs:716-750, "main"'s 32 // live address in *this* process). 33 println!( 34 "hotdemo pid={} aslr_reference={:#x}", 35 std::process::id(), 36 subsecond::aslr_reference() 37 ); 38 std::io::stdout().flush().ok(); 39 40 let patch_path = Path::new("run/hotdemo.patch.json"); 41 loop { 42 subsecond::call(tick); 43 std::io::stdout().flush().ok(); 44 45 if patch_path.exists() { 46 apply_pending_patch(patch_path); 47 } 48 49 std::thread::sleep(Duration::from_secs(1)); 50 } 51}
Read run/hotdemo.patch.json (a serialized subsecond_types::JumpTable),
apply it, and delete the file - the same drop-a-file protocol dx's
devtools websocket exists to avoid needing here (research/
subsecond-patch-build.md §4: jev's MCP server is the in-process
equivalent for apps/native; hotdemo does not even need that much).
58fn apply_pending_patch(patch_path: &Path) { 59 let bytes = match std::fs::read(patch_path) { 60 Ok(bytes) => bytes, 61 Err(e) => { 62 eprintln!("hotdemo: failed to read {}: {e}", patch_path.display()); 63 return; 64 } 65 }; 66 67 match serde_json::from_slice::<subsecond_types::JumpTable>(&bytes) { 68 Ok(table) => match unsafe { subsecond::apply_patch(table) } { 69 Ok(()) => println!("hotdemo: patch applied"), 70 Err(e) => eprintln!("hotdemo: patch apply failed: {e}"), 71 }, 72 Err(e) => eprintln!("hotdemo: bad patch json in {}: {e}", patch_path.display()), 73 } 74 75 if let Err(e) = std::fs::remove_file(patch_path) { 76 eprintln!("hotdemo: failed to remove {}: {e}", patch_path.display()); 77 } 78 std::io::stdout().flush().ok(); 79}