I have used C APIs to create a XPC server(mach service) as a launch daemon. I use dispatch_source_create ()
followed by dispatch_resume()
to start the listener. I dont have any code for cleaning up memory.
I want to make sure that the XPC server is shutdown gracefully, without any memory leaks. I know that launchd handles the cycle and the XPC framework takes care of XPC objects.
But do I need to do additional cleanup when the XPC listener is shutdown ?
How are you starting your daemon?
The correct way to do this is to load the daemon configuration into launchd
, either via a property list or by SMAppService
, and then either:
-
Have
launchd
start the daemon on demand. -
Set
KeepAlive
orRunAtLoad
, so that it starts as soon as you load it.
If you’re trying to start your daemon some other way — I see a lot of folks try to start a daemon from Terminal using sudo
— things will end badly on the XPC front. XPC requires that your named XPC endpoints be known to launchd
. I talk about this in detail in XPC and App-to-App Communication, linked to from the XPC Resources post.
Let’s start with the last case, namely the daemon crashing. Your daemon is managed by launchd
and that owns the named XPC endpoint. When your daemon checks in with launchd
— by calling one of the XPC listener APIs — it temporarily takes over responsibility for the endpoint. If the daemon crashes, that responsibility passes back to launchd
.
What happens in that case is determined by two things:
-
If your daemon is configured to run all the time, via
KeepAlive
,launchd
will start it again. There’s a rate limited on this to prevent the system glowing red hot if your daemon crashes on start. -
If not,
launchd
will monitor your on-demand sources, including this named XPC endpoint, and launch you when there’s demand.
Coming back to shut down and restart, the process is the same for both. The ideal pattern is for your daemon to support transactions. In that case launchd
will simply SIGKILL
your daemon when it wants it to stop. Or, if there’s a transaction open, it’ll SIGTERM
it and wait for the daemon to exit, resorting to SIGKILL
if that doesn’t happen promptly.
If your daemon doesn’t support transactions then launchd
always starts with the SIGTERM
.
In neither case do you have to do anything special with your XPC listener. If, for example, you were expecting to tidy things up after dispatch_main
returns, you’re in for a disappointment because that never returns (-: Note the DISPATCH_NORETURN
attribute on its declaration.
Finally:
Please make sure your Go project is set up to use the Apple linker. Failing that, make sure the final product:
-
Uses libSystem rather than making system calls directly
-
Has a main executable build UUID, per TN3178 Checking for and resolving build UUID problems
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"