Well, I don't think anyone is going to like this solution, but it seems to be working for me and it comes with a plausible explanation...

On 2009 Sep 16, at 14:07, Chris Kane wrote:

Go back to the main thread. Setup a oneshot NSTimer for the timeout period. Setup a notification handler to listen for the NSTaskDidTerminateNotification. If the timer fires first, kill the task, unregister the notification handler, etc. If the notification happens first, invalidate the timer, unregister the notification handler, etc. Don't run the run loop yourself. Let your code be event-driven.

This works fine for the main thread, but if I need to run this in a secondary thread or background tool, I still need something to make the thread's or tool's -[NSRunLoop runMode:beforeDate:] return, so that the thread or tool can exit. To solve that problem, at the end of my notification handler and timer handler, I send this message:

    [SSYRunLoopTickler tickle] ;

to the following class.  Further explanation is in the comments.


***** Header File *****

#import <Cocoa/Cocoa.h>

/*!
 @brief    A class providing a method to "tickle" the run loop with a
 dummy input source, causing a blocked -[NSRunLoop runMode:beforeDate:]
 to return.

@details This is useful in designs which worked in Mac OS 10.5 because
 they have run loops in background tools or secondary threads that were
 being run when needed by behind-the-scenes input sources.  These input
sources were apparently added by Cocoa in Mac OS 10.5, but they are not
 added in Mac OS 10.6.  This is probably because 10.6 is using Grand
 Central Dispatch or something else instead of run loops for whatever
 it's doing behind the scenes.
*/
@interface SSYRunLoopTickler : NSObject {
}

/*!
 @brief    Inserts a dummy input source into the current run loop in
 NSDefaultRunLoopMode, and sends a message to it, which will cause a
 blocked -[NSRunLoop runMode:beforeDate:] elsewhere in the program to
 return.

 @details  Removes the dummy input source after a delay of 0.0.
*/
+ (void)tickle ;

@end


***** Implementation File *****

#import "SSYRunLoopTickler.h"

@implementation SSYRunLoopTickler

+ (void)tickle {
    NSPort* sendPort = [NSMachPort port] ;
    [[NSRunLoop currentRunLoop] addPort:sendPort
                                forMode:NSDefaultRunLoopMode] ;
    NSPort* receivePort = [NSMachPort port] ;
NSPortMessage* message = [[NSPortMessage alloc] initWithSendPort:sendPort receivePort:receivePort components:nil] ; BOOL sentOk = [message sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]] ;
    if (!sentOk) {
        // Should actually return an NSError, but I don't think
        // this will ever happen in real life, so I'm just going
        // to log it.
        NSLog(@"%s failed to send its message.", __PRETTY_FUNCTION__) ;
    }

    [message release] ;

    // If I remove the port now, the desired "tickle" causing a
    // blocked -[NSRunLoop runMode:beforeDate:] to return will
    // not occur.  But if I do so with a delay of 0.0, it works.
    [self performSelector:@selector(removePort:)
               withObject:sendPort
               afterDelay:0.0] ;
}

/*!
 Just a debugging note:  If the +tickle message is sent from a
 secondary thread which exits, the delayed performance of this
 method will not occur, but that is OK because when its thread
 ends the system should remove the port.
 */
+ (void)removePort:(NSPort*)port {
    [[NSRunLoop currentRunLoop] removePort:port
                                   forMode:NSDefaultRunLoopMode] ;
}


@end

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to