views:

85

answers:

2

I have an application with 2 components: a desktop application that users interact with, and a background process that can be enabled from the desktop application. Once the background process is enabled, it will run as a user launch agent independently of the desktop app.

However, what I'm wondering is what to do when the user disables the background process. At this point I want to stop the background process but I'm not sure what the best approach is. The 3 options that I see are:

  1. Use the 'kill' command.
    • Direct, but not reliable and just seems somewhat "wrong".
  2. Use an NSMachPort to send an exit request from the desktop app to the background process.
    • This is the best approach I've thought of but I've run into an implementation problem (I'll be posting this in a separate query) and I'd like to be sure that the approach is right before going much further.
  3. Something else???

Thank you in advance for any help/insight that you can offer.

A: 

The daemon could handle quit apple events or listen on a CFMessagePort.

If you use signals you should handle the signal, probably SIG_QUIT, that is sent instead of just letting the system kill your process.

If you have cleanup that may take a while, use something other than signals. If you are basically just calling exit, then signals are fine.

If you already have a CFRunLoop going then use CFMessagePort. If you are already handling apple events than handle quit.

CFMessagePort is a wrapper around CFMachPort that provides a name and some other conveniences. You can also use the NS wrappers for either.

drawnonward
Thank you! Since I've got an NSRunLoop going I'll be sticking with NSMachPort. I looked into NSMessagePort but the current Apple <a href="http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSMessagePort_Class/Reference/Reference.html">documentation</a> tells developers to avoid its use in favour of NSMachPort and that it may be deprecated soon.
mcsheffrey
Thank you! Since I've got an NSRunLoop going I'll be sticking with NSMachPort. I looked into NSMessagePort but the current Apple [documentation](http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSMessagePort_Class/Reference/Reference.html) tells developers to avoid its use in favour of NSMachPort and that it may be deprecated soon.
mcsheffrey
A: 

I found an easier way to do this using an NSConnection object. I created a very simple ExitListener object with this header:

@interface ExitListener : NSObject {
    BOOL _exitRequested;
    NSConnection *_connection;
}

@property (nonatomic, assign) BOOL exitRequested;

- (void)requestExit;

@end

and this implementation:

@implementation ExitListener

@synthesize exitRequested = _exitRequested;

// On init we set ourselves up to listen for an exit
- (id)init {
    if ((self = [super init]) != nil) {
        _connection = [[NSConnection alloc] init];

        [_connection setRootObject:self];
        [_connection registerName:@"com.blahblah.exitport"];
    }
    return self;
}

- (void)dealloc {
    [_connection release];

    [super dealloc];
}

- (void)requestExit {
    [self setExitRequested:YES];
}

@end

To setup the listener, the background process simply allocates and inits an instance of the ExitListener. The desktop application then asks the background process to exit by making this call:

- (void)stopBackgroundProcess {
    // Get a connection to the background process and ask it to exit
    NSConnection *connection = [NSConnection connectionWithRegisteredName:@"com.blahblah.exitport" host:nil];
    NSProxy *proxy = [connection rootProxy];
    if ([proxy respondsToSelector:@selector(requestExit)]) {
        [proxy performSelector:@selector(requestExit)];
    }
}

Using NSMachPorts directly seemed to lead to far more problems in registering and obtaining references. I found that NSConnection is the simplest way to create a basic communication channel for the sort of situation that I needed to solve.

mcsheffrey