The generation counter is the detail I would have missed. I went the XPC route for a small root helper in one of my Mac apps, and what bit me there was the mirror of your HOST_PID check: the helper has to notice when the app goes away and undo what it changed, so it restores state when the client drops and again at boot. Does your core only exit when the host dies, or does it also tidy up work it left half done, like a session file mid-write or LSP children still running?
Thanks, glad that detail was useful. The generation counter guards against one specific thing: after a restart, a late frame or termination callback from the previous core process could otherwise land on the new session, for example failing its pending requests or resolving one with a stale reply. Everything carries the generation it was created under, so anything from an old process is just dropped.
On your question: the core does a bit of both, though nothing as thorough as restoring state at boot. When the host disappears, the watchdog cancels the shared shutdown token first, so in-flight tasks get about a second to stop before the process exits. Language servers are started in their own process group, so stopping a language session takes their children down with it. For session files, the store doesn't try to finish a half-done write. On the next open, it trims an incomplete trailing JSONL line back to the last complete record, and the files that get rewritten whole are written to a temp file and renamed into place.
I'm curious about your restore-at-boot path: how does the root helper know what it changed before it was killed? Do you keep a journal on disk, or recompute it from the system state?
No journal, it recomputes. The helper only touches one system power setting, and its safe value is known, so at boot it writes that value back before anything else, and the app has to ask for a hold again. Keeping the helper’s surface that small is what made a journal unnecessary. The trimmed trailing JSONL line is a nice trick, I’ll borrow it for my own session logs.
Makes sense, keeping the helper that small is a clean way to avoid the whole problem. Glad the JSONL trick helps, and thanks for sharing how you handled it.
Apps by Mo
Solo developer making calm, private Mac apps
The generation counter is the detail I would have missed. I went the XPC route for a small root helper in one of my Mac apps, and what bit me there was the mirror of your HOST_PID check: the helper has to notice when the app goes away and undo what it changed, so it restores state when the client drops and again at boot. Does your core only exit when the host dies, or does it also tidy up work it left half done, like a session file mid-write or LSP children still running?