Thanks for the fantastic reply, much appreciated and reassuring. I've gone back and forth between the XPC C-API and the NS-API for various reasons. I started with the C-API because it, originally, made more sense to me and it was the only way to use IOSurface at the time. When IOSurface support was added to the NS-API, I decided to give it another shot. But the "reply" model and "protocol" model for how the client should talk to the service never really felt right for the use-case I had in mind. I much prefer a more asynchronous model where the client sends a packaged/serialized "message" across to the service and, asynchronously, receives packaged/serialized "replies" on a different connection. Messages and replies are matched up using unique identifiers.
So I went back to using the C-API for awhile until Metal introduced shared events and shared textures, which appeared to only support the NS-API. With IOSurface now on the NS-API and Metal appearing to favour it, I took my C-API "architecture" and implemented it using the NS-API, which is where I stand today and I'm reasonably happy with it.
But, per this thread and the other one, there's no DispatchData support in the NS-API (at least there wasn't until you showed me the new API for xpc_type_t).
When all is said-and-done, I'm really just sending serialized data across as one big blob. I'll have to wire back up a way to "attach" IOSurfaces and shared Metal objects into the transport layer, but for those messages I might just fallback to a basic NSObject that implements NSSecureCoding to make things easier. (Message replies that include an IOSurface or shared Metal object don't have much other date in them, so there's no need for fast, efficient serialization of those messages.)
Finally, to tie things back to your reply, what is the NS-API equivalent of xpc_transaction_begin, and xpc_transaction_end? Some of the WWDC videos talk about "holding on to the message" in the XPC process to ensure that appropritate QOS boost levels are applied. I never really understood what that meant, but I assume if I'm "holding on to the message" in the service then perhaps I'm implicitly inside a transaction as well?
For example, my XPC service's protocol has a single method:
// Implemented by XPC Services.
@objc protocol ServiceRequestHandler {
func handleServiceMessage(_ message:ServiceMessage)
}
// Implemented by the App to receive response.
@objc protocol ServiceResponseHandler {
func handleServiceMessage(_ message:ServiceMessage)
}
// ServiceMessage is an NSObject that implements NSSecureCoding. It has a single NSData field in it
// representing serialized data. (And, optionally, an IOSSurface as mentioned above.)
// -------------------------------------------------------------------------------------------------
// On the service side, the implementation of handleServiceMessage looks roughly like this:
func handleServiceMessage(_ message:ServiceMessage) {
messagesToProcess.append(message)
processAnyPendingMessagesInASeparateThread()
}
My understanding of "holding on to a message" is that if Io keep a strong reference to `message`, then an implicit transation remains opens and my service remains boosted. As soon as I dequeue that message, process it and release it, then I'm no longer "holding on to it" and the XPC machinery can terminate the service or at least lower its QOS boost.
I've just assumed this by concluding that when `ServiceMessage` is serialized on the client using an XPC Coder, it must have some additional metadata attached to it that the XPC system tracks to determine whether the message is in-flight, delivered, handled and still alive or not.
For now I'll stick with the NS-API because I'm curious to explore shared Metal textures down the road, but perhaps something else compelling on the C-API will crop up and I'll swing back...