Reputation: 1514
I want to build an NSOperation that has a timeout of 10 seconds after it begins and could be ended by another thread at any point through an event. I also use an NSOperationQueue for managing more operations like this and it can only compute one at a time(maxConcurrentOperationCount = 1). For this I have thought about an implementation using dispatch_semaphore
's as it follows:
@implementation CustomOperation
dispatch_semaphore_t semaphore;
-(void) main {
@autoreleasepool {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shouldFinishWaiting:) name:@"myCustomEvent" object:nil];
semaphore = dispatch_semaphore_create(0);
[self doStuff];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)));
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"myCustomEvent" object:nil];
}
}
-(void) shouldFinishWaiting {
NSLog(@"[WatchOperation]: Should finish waiting! %@", self);
dispatch_semaphore_signal(semaphore);
}
@end
The problem I have is that once in many times when an user starts the application the first operation would not finish until the event gets triggered(and this could happen after 30 mins). The timeout would not be taken in consideration. I noticed this on logs from some users so I wasn't able to reproduce it. What could go wrong so that the dispatch_semaphore_wait
fails to execute?
Later edit: I mistakenly thought that the -doStuff
is async. It seems it is not.I replaced it with:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
[self doStuff];
});
, but the operation was already on the serial queue 'user initiated'. As I can see it creates another concurent thread, will that happen every time? Is this safe?
Upvotes: 3
Views: 3035
Reputation: 507
I don't think dispatch semaphore can go wrong. Maybe your -doStuff is taking too much time. Make sure you are doing the following:
1. The method [self doStuff];
is async and it dispatches to a DIFFERENT thread than the current one (dispatching to current thread doesn't really make sense if you want the 10 second timeout using the semaphore).
2. Make sure you keep checking for self.isCancelled in -doStuff.
Also, I would suggest that you take a slightly different design approach for your requirements (if I understand them correctly) --
1. Your NSOperation can always be cancelled from any external thread my calling cancel on the object, so there is no need for a complex NSNotification-based approach, just check for isCancelled and override the -cancel method.
2. For the 10 second timeout, you can use the semaphore approach, but just have a DIFFERENT thread doing the semaphore wait. That thread can then cancel your task from within after 10 seconds.
Upvotes: 1
Reputation: 52538
@selector(shouldFinishWaiting:) should be @selector (shouldFinishWaiting). No colon because the method has no arguments.
Upvotes: 0
Reputation: 52538
I assume your semaphore won't stay a global variable...
Depending on whether you are using 32 or 64 bit, NSEC_PER_SEC could be a 32 bit value, which overflows and turns into something negative when multiplied by 10. Replace 10 by 10.0. Might fix the problem, might not do anything at all.
Upvotes: 0