I’m designing a macOS utility that needs to run a bounded, one-shot diagnostic and collect its result.
The lifetime problem is:
- A controller asks for the diagnostic to start.
- The diagnostic may begin successfully.
- The controller can then fail immediately — potentially before it has retained the child’s PID or established output collection.
- The diagnostic may close stdin/stdout/stderr while continuing to run.
- The diagnostic must not become an unmanaged orphan if the controller disappears.
A focused test case uses Foundation.Process:
- Controller launches Child.
- Controller immediately exits with _exit(42), without waiting for Child or retaining its process identifier.
- Child calls setsid(), closes stdin/stdout/stderr, stays alive for up to 120 seconds, then exits.
What I’m trying to establish is the supported macOS lifecycle boundary, rather than inventing a cleanup scheme around PIDs.
Is there a supported per-user launchd or other OS-managed mechanism where the OS assumes responsibility for the job before the diagnostic process can start, such that the requesting controller may subsequently fail without leaving an unmanaged process?
Specifically:
- At what point does the OS own the lifetime relative to registration and process creation?
- What supported mechanism bounds or terminates the job if the requesting controller disappears?
- Is there a supported hard execution-time limit, rather than an idle-time concept?
- How are descendants or process groups contained, and does calling setsid() conflict with that containment?
- What completion or failure information remains available to a later controller after the original requester has died?
- Are there important per-user, privilege, sandboxing, signing, or macOS-version limitations?
I’m not reporting an Apple bug and I’m not asking for a custom recovery implementation. I’m trying to identify the documented supported lifetime mechanism and its guarantees and limitations before choosing an architecture.
Environment:
- macOS 26.6.2
- Apple silicon
- Foundation.Process is used by the focused reproducer
Apple Developer Technical Support suggested I start a new thread for this specific question in Processes & Concurrency.
So I’m trying to establish whether a per-user launchd job
If there were such a thing, it would be in launchd. However, launchd doesn’t have anything that’ll meet your specific requirements.
You can use an XPC service to tie the lifetime of the ‘child’ to the lifetime of the ‘parent’ (in quotes because there is not a child/parent relationship in this case) but launchd will automatically terminate the service when the ‘parent’ terminates.
You can avoid that by using a launchd agent, but in that case there’s no way to tell launchd to limit the runtime of the job [1]. You could add code to the program itself to do that — finally a use for SIGALRM (-: — but such code can never be 100% effective.
Moreover, using a launchd job has other consequences:
- You have to figure out a way to remove it when you’re done, or live with the fact that the job will be present always [2].
- The system alerts the user to apps installing potentially long-running programs like this (see System Settings > Login Items & Extensions > App Background Activity).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Well, that’s not quite true. You can apply the HardResourceLimits > CPU key, but that’s specified in terms of CPU time rather than ‘wall’ time, which means it’ll never kick in if the job blocks indefinitely.
[2] This isn’t a huge problem because the process can terminate, after which the only resource consumed by the job is some memory within launchd.