• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])


  • Subject: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
  • From: Jerry Krinock <email@hidden>
  • Date: Thu, 17 Sep 2009 22:59:08 -0700


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

On Sep 14, 2009, at 6:29 PM, I wrote:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                         beforeDate:limitTime] ;
// The above will block and be run to here either due to
// the posting of an NSTaskDidTerminateNotification, ...

That is not a valid assumption (described in the comments and apparent from the rest of the code).

Yup. It works in 10.5 but not 10.6. Something is tickling this run loop in 10.5, within a few milliseconds of the NSTaskDidTerminateNotification, so I thought it was the notification itself. Re-reading about Run Loops in Threading Programming Guide [1], I learn that notifications are in fact *not* input sources. The difference in 10.6 is probably explained in the 10.6 Cocoa Foundation Release Notes [2], which I would paraphrase as "run loops which were tickled into running by mysterious input sources in 10.5 may no longer get those tickles in 10.6." Someone please correct me if I misunderstood.


So, I'd like to implement Chris Kane's preferred solution ....

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.

That would certainly work, but I sometimes need to run this show in a background worker tool, or in secondary thread -- not just in the main thread of an app. How can I detect notifications and use them for controlling program flow in a non-event-driven environment?


To illustrate the problem, I've appended a demo program [3]. Like my original code, the demo works in 10.5 -- the timeout and NSTaskDidTerminateNotification are received *and* the run loop runs...

21:35:56.969 TestTool[366:10b] ver 1.2
21:35:56.982 TestTool[366:10b] Will run run loop
21:35:56.985 TestTool[366:10b] top of loop
21:35:57.122 TestTool[366:10b] Succeeded : Task with timeout: 1.00 cmd: /bin/sleep 0.10
21:35:57.124 TestTool[366:10b] moreInputSources = 1
21:35:57.164 TestTool[366:10b] nTasks = 1
21:35:57.171 TestTool[366:10b] top of loop
21:35:57.481 TestTool[366:10b] Timed out : Task with timeout: 0.50 cmd: /bin/sleep 0.80
21:35:57.795 TestTool[366:10b] moreInputSources = 1
21:35:57.797 TestTool[366:10b] nTasks = 0
21:35:57.798 TestTool[366:10b] All tasks are done. Exitting.


Running same executable in 10.6, the run loop never runs, and thus the program never exits...

22:05:14.137 TestTool[141:903] ver 1.2
22:05:14.152 TestTool[141:903] Will run run loop
22:05:14.154 TestTool[141:903] top of loop
22:05:14.254 TestTool[141:903] Succeeded : Task with timeout: 1.00 cmd: /bin/sleep 0.10
22:05:14.643 TestTool[141:903] Timed out : Task with timeout: 0.50 cmd: /bin/sleep 0.80


P.S. Interesting how run loops work. As you can see from the code, after logging 22:05:14 "top of loop", the next statement invokes - runMode:beforeDate: which apparently blocks forever because the NSLog in the following line never logs. But, while it's blocked there, it still receives and processes the NSTaskDidTerminateNotification and timer firing.

Thanks very much,

Jerry Krinock

1. http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#/ /apple_ref/doc/uid/10000057i-CH16-SW19

2. http://developer.apple.com/mac/library/releasenotes/Cocoa/Foundation.html See the end of the second paragraph of the section named "Concurrency, GCD, and Run Loop Cautions (New since November seed)"

3.  Demo Program

/* This program creates two TaskWrapper objects. Each TaskWrapper
runs an NSTask launching /bin/sleep. The first one succeeds because
its sleep argument of 0.1 seconds is less than its timeout of 1.0 seconds.
The second one times out because its sleep argument of 0.8 seconds is greater
than its timeout time of 0.5 seconds.
*/


#import <Cocoa/Cocoa.h>

static NSMutableArray* activeTaskWrappers ;

@interface TaskWrapper : NSObject {
    NSTask* m_task ;
    NSTimer* m_timeoutTimer ;
    NSString* m_signature ;
    NSString* m_status ;
}

@property (retain) NSTask* task ;
@property (retain) NSTimer* timeoutTimer ;
@property (copy) NSString* signature ;
@property (copy) NSString* status ;

@end

@implementation TaskWrapper

@synthesize task = m_task ;
@synthesize timeoutTimer = m_timeoutTimer ;
@synthesize signature = m_signature ;
@synthesize status = m_status ;

- (void)dealloc {
    [m_task release] ;
    [m_timeoutTimer release] ;
    [m_signature release] ;

    [super dealloc] ;
}

- (void)doShellTaskCommand:(NSString*)command
                 arguments:(NSArray*)arguments
                   timeout:(NSTimeInterval)timeout {
    // Create and configure a task
    NSTask* task = [[NSTask alloc] init] ;
    [self setTask:task] ;
    [task release] ;
    [task setLaunchPath:command] ;
    [task setArguments:arguments] ;
    NSString* signature = [NSString stringWithFormat:
                           @"Task with timeout: %0.2f cmd: %@ %@",
                           timeout,
                           command,
                           [arguments componentsJoinedByString:@" "]] ;
    [self setSignature:signature] ;

// Add observer for task's self-termination
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(taskWrapperDone:)
name:NSTaskDidTerminateNotification
object:task] ;


// Add timer for task timing out
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:self
selector:@selector(timeout:)
userInfo:nil
repeats:NO] ;
[self setTimeoutTimer:timer] ;


    // Launch the task
    [task launch] ;
}

- (void)taskWrapperDone:(NSNotification*)note {
    [[self timeoutTimer] invalidate] ;
    [self setStatus:@"Succeeded"] ;
    NSLog(@"%@ : %@", [self status], [self signature]) ;
    [[NSNotificationCenter defaultCenter] removeObserver:self] ;
    [activeTaskWrappers removeObject:self] ;
}

- (void)timeout:(NSNotification*)note {
    [self setStatus:@"Timed out"] ;
    NSLog(@"%@ : %@", [self status], [self signature]) ;
    [[NSNotificationCenter defaultCenter] removeObserver:self] ;
    [activeTaskWrappers removeObject:self] ;
}

@end


int main(int argc, const char *argv[]) { NSLog(@"ver 1.2") ; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;

    NSString* sleepTimeString ;
    NSTimeInterval timeout ;
    NSTimeInterval sleepTime ;
    NSDate* startDate ;
    TaskWrapper* taskWrapper ;

    activeTaskWrappers = [[NSMutableArray alloc] init] ;

    // Begin a taskWrapper which will succeed
    taskWrapper = [[TaskWrapper alloc] init] ;
    [activeTaskWrappers addObject:taskWrapper] ;
    [taskWrapper release] ;
    timeout = 1.0 ;
    sleepTime = 0.1 ;
    sleepTimeString = [NSString stringWithFormat:@"%0.2f", sleepTime] ;
    startDate = [NSDate date] ;
    [taskWrapper doShellTaskCommand:@"/bin/sleep"
                    arguments:[NSArray arrayWithObject:sleepTimeString]
                      timeout:timeout] ;

// Begin a taskWrapper which will time out
taskWrapper = [[TaskWrapper alloc] init] ;
[activeTaskWrappers addObject:taskWrapper] ;
[taskWrapper release] ;
timeout = 0.5 ;
sleepTime = 0.8 ;
sleepTimeString = [NSString stringWithFormat:@"%0.2f", sleepTime] ;
[taskWrapper doShellTaskCommand:@"/bin/sleep"
arguments:[NSArray arrayWithObject:sleepTimeString]
timeout:timeout] ;


    NSLog(@"Will run run loop") ;

// Although it is customary to write the following loop as one line of code,
// I blew it up so I could understand what was happening.
while (YES) {
NSLog(@" top of loop") ;
BOOL moreInputSources = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate: [NSDate distantFuture]] ;
NSLog(@" moreInputSources = %d", moreInputSources) ;
if (!moreInputSources) {
break ;
}


        // The "keep running" condition ...
        NSInteger nTasks = ([activeTaskWrappers count] > 0) ;
        NSLog(@"   nTasks = %d", nTasks) ;
        if (nTasks < 1) {
            break ;
        }
    }

    NSLog(@"All tasks are done.  Exitting.") ;

    [activeTaskWrappers release] ;
    [pool release] ;

    return 0 ;
}

_______________________________________________

Cocoa-dev mailing list (email@hidden)

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:
This email sent to email@hidden


  • Follow-Ups:
    • [SOLVED (but not pretty)] Running run loops in 10.6
      • From: Jerry Krinock <email@hidden>
    • Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
      • From: Jerry Krinock <email@hidden>
References: 
 >NSTask is "Thread Unsafe". What does that mean? (From: Jerry Krinock <email@hidden>)
 >Re: NSTask is "Thread Unsafe". What does that mean? (From: "Adam R. Maxwell" <email@hidden>)
 >Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...) (From: Jerry Krinock <email@hidden>)
 >Re: Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...) (From: Chris Kane <email@hidden>)

  • Prev by Date: Re: Making a new "ICNS file" with NSImage
  • Next by Date: NSView / NSControl / NSCell drawing question
  • Previous by thread: Re: Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...)
  • Next by thread: Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
  • Index(es):
    • Date
    • Thread