20

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.

Jonas Gardner
  • 2,458
  • 5
  • 22
  • 28

1 Answers1

31

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.

Community
  • 1
  • 1
mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
  • 1
    Ah I see. Because every dispatch_suspend 'increments the suspension count of the object', there has to be a 'dispatch_resume' to balance the suspend. – Jonas Gardner Mar 05 '12 at 19:04
  • Is it feasible to cancel or suspend the timer within its event_handler block or does that have to happen outside the block? – Jonas Gardner Mar 05 '12 at 19:05
  • That's right yes. See my updated answer. Whilst the docs don't actually state (that I can find) that you can't release when it's suspended, I suspect it's got some internal state that causes badness if you did. – mattjgalloway Mar 05 '12 at 19:06
  • @JonasGardner - updated answer again. Pretty sure you can suspend inside the event handler, yes. As the suspension happens after completion of any blocks currently running on the queue at the time of the call. – mattjgalloway Mar 05 '12 at 19:09