My installer load XPC service and XPC client that attempt to call remote xpc method on that service.
However, the service my be loaded arbitrarily and the client may get invalid connection since the service hasn't loaded yet. So far I haven't found any way get service-load indication, so I'm calling the retry method recursively on the connection invalidationHandler. Is this the correct approach ? is there any wait-for-service event I can wait for ?
+(void) callXpcWithRetry {
NSXPCConnection* hubConnection = [[NSXPCConnection alloc] initWithMachServiceName:@"com.bla.myservice" options:0];
hubConnection.remoteObjectInterface = getInterface();
[hubConnection setInvalidationHandler:^{
// No one is listening. connection cannot be established
NSLog(@"Connection to keystore hub service invalidated .. retry in 5");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[ServiceDelegate registerBiometricKeyStoreRetry];
});
}];
[hubConnection resume];
id<myXpcProtocol> hub = [hubConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
}];
[hub xpcProtocolMethodForUser:NSUserName()];
}
You could just try to connect to it as below. This is real quick.
// Connect to the service, this also checks to see if it is available
mach_port_t connect_to_service ( const char * service_name )
{
mach_port_t bs_port, service_port;
kern_return_t err;
task_get_bootstrap_port ( mach_task_self (), & bs_port );
err = bootstrap_look_up ( bs_port, service_name, & service_port );
if ( err == KERN_SUCCESS )
{
return service_port;
}
else
{
return MACH_PORT_NULL;
}
}
Here is the full test I used.
#import <Foundation/Foundation.h>
#import <bootstrap.h>
// Connect to the service, this also checks to see if it is available
mach_port_t connect_to_service ( const char * service_name )
{
mach_port_t bs_port, service_port;
kern_return_t err;
task_get_bootstrap_port ( mach_task_self (), & bs_port );
err = bootstrap_look_up ( bs_port, service_name, & service_port );
if ( err == KERN_SUCCESS )
{
return service_port;
}
else
{
return MACH_PORT_NULL;
}
}
// Start the service
start_service ( const char * service_name, dispatch_semaphore_t s )
{
// Simulate delay
[NSThread sleepForTimeInterval:10];
mach_port_t service_port = MACH_PORT_NULL;
kern_return_t err;
err = bootstrap_check_in ( bootstrap_port, service_name, & service_port );
NSLog ( @"%s port %d err %d", service_name, service_port, err );
NSLog ( @"Server up and running - signal semaphore" );
dispatch_semaphore_signal ( s );
// Simulate running server
while ( YES )
{
[NSThread sleepForTimeInterval:60];
}
}
int main(int argc, const char * argv[])
{
@autoreleasepool
{
// insert code here...
NSLog(@"Hello, World!");
// Configuration
char * service_name = "com.hopla.test";
mach_port_t port;
// Check it
NSLog ( @"Connecting to %s that is down", service_name );
port = connect_to_service ( service_name );
NSLog ( @"%s available : %@", service_name, port == MACH_PORT_NULL ? @"NO" : @"YES" );
// The semaphore fires when the server is up, but is not used here
dispatch_semaphore_t s = dispatch_semaphore_create ( 0 );
// Starting service on background thread
dispatch_async( dispatch_queue_create( "bak",
dispatch_queue_attr_make_with_qos_class( DISPATCH_QUEUE_CONCURRENT,
QOS_CLASS_DEFAULT,
DISPATCH_QUEUE_PRIORITY_DEFAULT ) ), ^ {
NSLog ( @"Starting %s", service_name );
start_service ( service_name, s );
} );
// Repeatedly checks until the service is up
// *** do something like in this loop ***
while ( YES )
{
NSLog ( @"Connecting to %s that may be up", service_name );
port = connect_to_service ( service_name );
NSLog ( @"%s available : %@", service_name, port == MACH_PORT_NULL ? @"NO" : @"YES" );
if ( port == MACH_PORT_NULL )
{
[NSThread sleepForTimeInterval:5];
}
else
{
break;
}
}
// Final test - should be up
port = connect_to_service ( service_name );
NSLog ( @"%s fin available : %@", service_name, port == MACH_PORT_NULL ? @"NO" : @"YES" );
}
return 0;
}
I think you should do something as I do in the test (in main
method and marked with ***). On some background thread, check repeatedly but with a decent loop if the server is up. Then you can signal a semaphore or use a completion or do whatever needs to be done once the server is up. Elsewhere you can wait on the semaphore or whatever is required.