NSXPCConnection multiple instances.

My application handles multiple connections through the USB ports as serial devices that use termios, the application needs to send and receive a large amount of data for each open connection, but in very rare cases the process that handles the data received through a specific USB port crashes, collapsing the application and affecting the other open connections. To minimize the damage, I intend to use NSXPCConnection instances. My question to this forum is: How many similar instances of NSXPCConnection can be opened for the same application?

Accepted Reply

Quite possibly.

OK, I had a chance to look into this in depth last week. Unfortunately the news is not great. The strategy I outlined in my 20 Nov 2017 post works for

launchd
daemons and agents but it does not work for XPC Services. The problem here is that that the XPC Service’s name is only registered in the app’s context. It is not registered in the XPC Service’s context, so a child process launched by the XPC Service can’t connect to it.

The enhancement request that ManuelCSolis filed about this back on 21 Nov 2017 (r. 35659284) is still open and we’re using it to track the requirement for a supported way to implement multiple instances. If you have a product that would benefit from this, it wouldn’t hurt to file your own enhancement request describing its specific requirements.

In the meantime, if you need to do this you should get in touch with DTS. While there’s no general solution to this problem, in some cases their may be a special-case solution that meets your needs.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

How many similar instances of NSXPCConnection can be opened for the same application?

That depends on what you mean by “similar”. Looking at your big picture goal I suspect that you’re trying to use an XPC Service to run your serial connections, as discussed in the Creating XPC Services section of the Daemons and Services Programming Guide. Is that right?

If so, be aware that the public XPC Service mechanism does not support multiple instances. That is, if an app has XPC Services nested within the app, an instance of that app can only instantiate a single instance of each of those services. So, for example, if you have an XPC Service that runs a serial connection, you must either:

  • Restrict yourself to one serial connection

  • Run multiple serial connections in the same service

Clearly both of these are suboptimal. Specifically, the second option means that, while your app is isolated from a crash in your service, a crash for serial connection A will also cause serial connections B and C to fail.

IMPORTANT If you’d like us to support multiple instances of a single service in the future, I encourage you to file an enhancement request describing your requirements. Please post your bug number, just for the record.

It is possible to work around this. One easy workaround would be to have multiple services that all run the same code. It’s unlikely that a Mac would have more than, say, 10 serial ports attached at once, so this won’t get too far out of control. Also, you can use a framework to share code between the services, so the disk space impact won’t be prohibitive.

The alternative is less easy, namely, you have a single service that acts as a broker and launches child processes to run each serial connection. This breaks down as follows:

  1. The app connects to the main service and asks it to start a new serial connection.

  2. The service launches a child process (using

    NSTask
    or any other
    posix_spawn
    wrapper).
  3. That child ‘checks in’ with the main service.

  4. The child creates an anonymous XPC listener (

    +[NSXPCListener anonymousListener]
    ), gets an endpoint for that listener (
    endpoint
    ) and passes that endpoint to the service.
  5. The service then passes that endpoint back to the app.

  6. The app can then connect to the child directly (

    -[NSXPCConnection initWithListenerEndpoint:
    ).

Now, if the broker service goes down then that will take all of the connections with it, but that should be relatively robust because it’s not actually doing any work with the serial port.

Getting this to work reliably will involve a bunch of complex code, but I don’t see any unsurmountable obstacles.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks again eskimo... This is the Suggestion track number in bug reporter: 35659284.

I will do my best to implement your suggestion, I will comment on the results.


Thanks a lot.


Manuel C.

Hi Quinn - thanks for outlining the solution here. I'm thinking about taking a crack at something similar. Specifically I want to be able to run AppleScript on multiple processes, one process per script. I'm a little scared off by "Getting this to work reliably will involve a bunch of complex code" ... any tips for what I should be looking out for? Thanks!


Daniel

I found this open source project which appears to implement something along the lines of what Quinn has recommended here: https://github.com/OpenEmu/OpenEmuXPCCommunicator

any tips for what I should be looking out for?

I have not had need to work through this process myself, so I don’t have any direct experience to share.

ps Please don’t forget to file an enhancement request for this.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi eskimo,


I am trying to build this solution you suggested, but am a bit unclear as to what you mean by:


  1. That child ‘checks in’ with the main service.
  2. The child creates an anonymous XPC listener (
    +[NSXPCListener anonymousListener]
    ), gets an endpoint for that listener (
    endpoint
    ) and passes that endpoint to the service.


Is this by letting the launched endpoint task connect to the broker task via NSXPCConnection(serviceName:)?


It does not seem to be able to connect to the broker XPC service, I think because its a child task and doesn't have permission to talk to it? Because of this, I can't pass the endpoint back to the main application.


Any idea what might be wrong?


The error I am getting back in the endpoint task is:

Received error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service on pid 0 named com.***.yyy.broker was invalidated." UserInfo={NSDebugDescription=The connection to service on pid 0 named com.***.yyy.broker was invalidated.}


The broker is running when this error occurs and the main application can connect to it just fine (and launches it automatically).

It does not seem to be able to connect to the broker XPC service, I think because its a child task and doesn't have permission to talk to it?

Quite possibly. Alas, I must admit to have not actually tested this, and I just don’t have time to do that here on DevForums. If you open a DTS tech support incident, that’ll give me a chance to dig into it properly.

Oh, one thing before you do that: Try doing the connect back with

-initWithMachServiceName:options:
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Quite possibly.

OK, I had a chance to look into this in depth last week. Unfortunately the news is not great. The strategy I outlined in my 20 Nov 2017 post works for

launchd
daemons and agents but it does not work for XPC Services. The problem here is that that the XPC Service’s name is only registered in the app’s context. It is not registered in the XPC Service’s context, so a child process launched by the XPC Service can’t connect to it.

The enhancement request that ManuelCSolis filed about this back on 21 Nov 2017 (r. 35659284) is still open and we’re using it to track the requirement for a supported way to implement multiple instances. If you have a product that would benefit from this, it wouldn’t hurt to file your own enhancement request describing its specific requirements.

In the meantime, if you need to do this you should get in touch with DTS. While there’s no general solution to this problem, in some cases their may be a special-case solution that meets your needs.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I hit the same issue in HandBrake (a sandboxed video transcoding app). Fortunately I need at most a few xpc istances, so I packed 4 separate xpc services (~ 80 KB each, linked to a framework to avoid duplicating most of the binary).

I'll fill an enhancement request too.