Challenge: Block Main Thread while Work is done, with Timeout
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