I'm trying to cancel and then release a suspended timer but when I invoke 'dispatch_release' on it, I immediately get EXC_BAD_INSTRUCTION.
Is this not a valid set of actions to take on a timer?
Timer creation & suspension:
@interface SomeClass: NSObject { }
@property (nonatomic, assign) dispatch_source_t timer;
@end
// Class implementation
@implementation SomeClass
@synthesize timer = _timer;
- (void)startTimer
{
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0, 0, globalQ);
dispatch_time_t startWhen = dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1);
dispatch_source_set_timer(_timer, startWhen, 1 * NSEC_PER_SEC, 5000ull);
dispatch_source_set_event_handler(_timer, ^{
// Perform a task
// If a particular amount of time has elapsed, kill this timer
if (timeConstraintReached)
{
// Can I suspend this timer within it's own event handler block?
dispatch_suspend(_timer);
}
});
dispatch_resume(_timer);
}
- (void)resetTimer
{
dispatch_suspend(_timer);
dispatch_source_cancel(_timer);
// dispatch_release causes
// 'EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
dispatch_release(_timer);
self.timer = nil;
}
@end
Additionally, can I invoke dispatch_suspend within a timer source's event_handler block?
Any help would be appreciated.
The reason it crashes is because of this code:
void
_dispatch_source_xref_release(dispatch_source_t ds)
{
if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) {
// Arguments for and against this assert are within 6705399
DISPATCH_CLIENT_CRASH("Release of a suspended object");
}
_dispatch_wakeup(ds);
_dispatch_release(ds);
}
So, you can't release a dispatch_source_t
that has been suspended. You probably want to just not suspend it in resetTimer
I guess.
Whilst I can't find anything in the docs for why they have written it like this (and the comment alludes to the pros and cons being in a radar we'll never see), all I can do is refer to the docs where it says:
You can suspend and resume the delivery of dispatch source events temporarily using the dispatch_suspend and dispatch_resume methods. These methods increment and decrement the suspend count for your dispatch object. As a result, you must balance each call to dispatch_suspend with a matching call to dispatch_resume before event delivery resumes.
Whilst that doesn't say you can't release a dispatch source that's been suspended, it does say you have to balance each call so I'm assuming it's something along the lines of it's using a dispatch semaphore under-the-hood which have to be balanced before they can be released. That's just my guess though :-).
As for "can I invoke dispatch_suspend within a timer source's event_handler block". I'm pretty sure you can, yes, as per the docs for dispatch_suspend
:
The suspension occurs after completion of any blocks running at the time of the call.