Last active
December 7, 2025 01:37
-
-
Save zigster64/8d9bbf9477e0149fc92038a56a209a28 to your computer and use it in GitHub Desktop.
Zig binary file watcher - reboot on recompile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // For dev use - add a call to this watchLoop thread in the main() of your program | |
| // Whenever the app is re-compiled, it will detect the change and re-execute itself automatically | |
| // | |
| // For prod or UAT - can use this too (if its safe to simply re-exec in the middle of doing work) | |
| // | |
| // Makes deployments pretty simple - just drop the new binary in production and it respawns within 2 secs max | |
| // Obviously - extend this for your real prod case to do a graceful shutdown, depending on what that | |
| // means for your app | |
| fn watchLoop(self: *Watcher) !void { | |
| const cwd = std.fs.cwd(); | |
| std.Thread.sleep(2 * std.time.ns_per_s); | |
| var buffer: [255]u8 = undefined; // select appropriate size here | |
| const path = try std.fs.selfExePath(&buffer); | |
| var stat = try cwd.statFile(path); | |
| const initial_inode = stat.inode; | |
| const initial_mtime = stat.mtime; | |
| while (true) { | |
| std.Thread.sleep(2 * std.time.ns_per_s); | |
| stat = try cwd.statFile(path); | |
| const inode_changed = (stat.inode != initial_inode); | |
| const mtime_changed = (stat.mtime > initial_mtime); | |
| if (inode_changed or mtime_changed) { | |
| // log reboot here ♻️ | |
| const args = try std.process.argsAlloc(allocator); | |
| const self_path = try std.fs.selfExePathAlloc(allocator); | |
| var exec_args: std.ArrayList([]const u8) = .empty; | |
| try exec_args.append(allocator, self_path); | |
| for (args[1..]) |arg| { | |
| try exec_args.append(allocator, arg); | |
| } | |
| // INSERT HERE - anything you need to do for a graceful shutdown first | |
| // Reboot ! | |
| return std.process.execv(allocator, exec_args.items); | |
| // The rebooted process will have the exact same PID as the existing process | |
| // If you want a new PID, use fork() instead | |
| } | |
| } | |
| } | |
| // example of use from main ... using ENV var as feature flag | |
| fn main() !void { | |
| var allocator = ... get me an allocator | |
| if (std.posix.getenv("WATCH")) |_| { | |
| const thread = try std.Thread.spawn(.{}, watchLoop, .{allocator}); | |
| thread.detach(); | |
| } | |
| .. do the app stuff now | |
| .. app will reboot on successful re-compile | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment