• 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
Challenge: Block Main Thread while Work is done, with Timeout
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Challenge: Block Main Thread while Work is done, with Timeout


  • Subject: Challenge: Block Main Thread while Work is done, with Timeout
  • From: Jerry Krinock <email@hidden>
  • Date: Thu, 23 Jul 2009 16:44:12 -0700

I'd often like to block the main thread while another thread or process performs a little task, but subject to a short timeout. A few weeks ago, I was able to achieve this by "running" the main thread's run loop while an NSTask completed. But I did this after hours of experimenting and still don't understand how it works. Today, when I tried to generalize the code, waiting for a little worker thread instead of an NSTask, my run loop just gets stuck.

I know the trouble is that, although I have read the NSRunLoop documentation and related material in the Threading Programming Guide many times, I just don't understand the magic behind this:

   while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                   beforeDate:[NSDate distantFuture]])
   {
     // do something
   }

I know that it only runs when an "input source" fires, and so I believe I need to add an input source, but when I try to do that I end up creating ports and distributed objects, which is obviously wrong.

Below is a little demo project. If you look at the end, in main(), you'll see that, in Test #1, I start a job with a duration of 1.0 seconds and a timeout of 2.0 seconds. Expected result: The job should complete and program should continue. Actual result: Run loop never "fires", so the program gets stuck. (In Test #2, I would start a similar job which ^should^ time out.)

Can anyone show how to fix this?

Sincerely,

Jerry Krinock


#import <Cocoa/Cocoa.h>

NSString* const SSYThreadBlockerIsDoneNotification = @"SSYThreadBlockerIsDoneNotification" ;

@interface SSYThreadBlocker : NSObject
{
    // To do:  @synchronize access to these?
    BOOL isDone ;
    BOOL didTimeout ;
}

@property BOOL isDone ;
@property BOOL didTimeout ;

@end

@implementation SSYThreadBlocker

@synthesize isDone ;
@synthesize didTimeout ;


- (void)timeout:(NSTimer*)timeoutTimer { NSLog(@"380 %s", __PRETTY_FUNCTION__) ; if (![self isDone]) { [self setDidTimeout:YES] ; } [self setIsDone:YES] ; }

- (void)workerDone:(NSNotification*)note
{
    NSLog(@"533 %s", __PRETTY_FUNCTION__) ;
    [self setIsDone:YES] ;
}


/*! @brief Starts a job on another thread and blocks the current thread until the job notifies that it is complete, subject to a timeout

@details When the job is complete, the worker must post an
SSYThreadBlockerIsDoneNotification notification from the default
notification center, with the notification object set to itself.
@param worker The target which will perform the job.
@param selector The selector which defines the job. If no
garbage collection, this selector must create and destroy an
autorelease pool.
@param object A parameter which will be passed to selector
@param workerThread The thread on which the job will be performed.
If you pass nil, a temporary thread will be created.
@param timeout The time before the job is aborted.
@result YES if the job completed, NO if it timed out.
*/
+ (BOOL)blockUntilWorker:(id)worker
selector:(SEL)selector
object:(id)object
thread:(NSThread*)workerThread
timeout:(NSTimeInterval)timeout
{
SSYThreadBlocker* instance = [[SSYThreadBlocker alloc] init] ;
NSTimer* timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:instance
selector:@selector(timeout:)
userInfo:nil
repeats:NO] ;


[[NSNotificationCenter defaultCenter] addObserver:instance
selector:@selector(workerDone:)
name:SSYThreadBlockerIsDoneNotification
object:worker] ;


// Begin Work
if (workerThread)
{
[worker performSelector:selector
onThread:workerThread
withObject:object
waitUntilDone:NO] ;
}
else
{
// Default if no workerThread given is to create one
workerThread = [[[NSThread alloc] initWithTarget:worker
selector:selector
object:object] autorelease] ;
// Name the thread, to help in debugging.
[workerThread setName:@"Worker created by SSYThreadBlocker"] ;
[workerThread start] ;
}


    // Re-run the current run loop.  (Magic)
    NSLog(@"1415 Will 'run' the Run loop") ;
    while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                    beforeDate:[NSDate distantFuture]])
    {
        NSLog(@"2813: Run loop did fire") ;
        if ([instance isDone]) {
            break ;
        }
    }
    NSLog(@"1731 Run loop is done") ;

    [timeoutTimer invalidate] ;

[[NSNotificationCenter defaultCenter] removeObserver:instance
name:SSYThreadBlockerIsDoneNotification
object:timeoutTimer] ;
BOOL didTimeout_ = [instance didTimeout] ;
[instance release] ;


    return (!didTimeout_) ;
}

@end


@interface WorkerDemo : NSObject {}

- (void)doWorkForTimeInterval:(NSNumber*)interval ;

@end


@implementation WorkerDemo

- (void)doWorkForTimeInterval:(NSNumber*)interval
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
    NSLog(@"2308: Beginning work") ;

    NSTimeInterval intervalValue = [interval doubleValue] ;
    usleep(1e6 * intervalValue) ;

[[NSNotificationCenter defaultCenter] postNotificationName:SSYThreadBlockerIsDoneNotification
object:self] ;


    NSLog(@"2557: Ending work") ;
    [pool release] ;
}

@end



int main (int argc, const char * argv[])
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;

    WorkerDemo* workerDemo = [[WorkerDemo alloc] init] ;
    NSTimeInterval duration ;
    NSTimeInterval timeout ;
    BOOL ok ;

NSLog(@"Starting Test #1") ;
duration = 1.0 ;
timeout = 2.0 ;
ok = [SSYThreadBlocker blockUntilWorker:workerDemo
selector:@selector(doWorkForTimeInterval:)
object:[NSNumber numberWithDouble:duration]
thread:nil // SSYThreadBlocker will create a thread
timeout:timeout] ;
NSLog(@"Job duration=%0.0f timeout=%0.0f succeeded=%d",
duration,
timeout,
ok) ;



NSLog(@"Starting Test #2") ;
duration = 3.0 ;
timeout = 2.0 ;
ok = [SSYThreadBlocker blockUntilWorker:workerDemo
selector:@selector(doWorkForTimeInterval:)
object:[NSNumber numberWithDouble:duration]
thread:nil // SSYThreadBlocker will create a thread
timeout:timeout] ;
NSLog(@"Job duration=%0.0f timeout=%0.0f succeeded=%d",
duration,
timeout,
ok) ;


    [pool release] ;
}

_______________________________________________

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:
    • Re: Challenge: Block Main Thread while Work is done, with Timeout
      • From: Dave Camp <email@hidden>
    • Re: Challenge: Block Main Thread while Work is done, with Timeout
      • From: Ken Thomases <email@hidden>
  • Prev by Date: Re: Recording phone calls
  • Next by Date: Re: Recording phone calls
  • Previous by thread: Re: SpeechSynthesis
  • Next by thread: Re: Challenge: Block Main Thread while Work is done, with Timeout
  • Index(es):
    • Date
    • Thread