Dispatch Source deallocated

Hi,

I'm using a dispatch source as a timer, I noticed that when the object that contains it is deallocated I get a crash:


libdispatch.dylib`_dispatch_xref_dispose:
    0x100134ac8 <+0>:  ldr    w8, [x0, #48]
    0x100134acc <+4>:  cmp    w8, #2


This happens only if the dispatch source had been suspended, according to the documentation (https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html)

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


So I'm wondering if it's not possible to dispatch_source_cancel a suspended dispatch source. Actually the crash is when dealloc ends, the dispatch_source_cancel seems to always work.


Here's my code:


@interface DispatchTimer(){
    dispatch_source_t dispatchSource;
    NSString *uniqueId;
}
@end
@implementation DispatchTimer
- (void)dealloc {
    NSLog(@"DEALLOCATED DispatchTimer");
   
    if(dispatch_source_testcancel(dispatchSource) == 0){
        dispatch_source_cancel(dispatchSource);
    }
}
- (instancetype)init{
    self = [super init];
    if (self) {
        uniqueId = [[NSUUID UUID] UUIDString];
       
        dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
                                                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
       
        dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
        uint64_t intervalTime = (int64_t)(1 * NSEC_PER_SEC);
        dispatch_source_set_timer(dispatchSource, startTime, intervalTime, 0);
       
        __weak DispatchTimer *weakSelf = self;
       
        dispatch_source_set_event_handler(dispatchSource, ^{
            [weakSelf update];
        });
       
        dispatch_resume(dispatchSource);
    }
    return self;
}
- (void)stop{
    dispatch_suspend(dispatchSource);
}
- (void)update{
    NSLog(@"Update %@", uniqueId);
}


If method 'stop' is called when the DispatchTimer is deallocated I get the crash, if stop is not called everything seems to be fine.

In the docs they mention dispatch_release but I get an error when using it in ARC.


Thanks for any help.

Accepted Reply

I think this is the droid you’re looking for.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • This link no longer works after the recent, uhm, forum "improvements" cough. I'm stumbling over a similar problem… could you please advise?

Add a Comment

Replies

I think this is the droid you’re looking for.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • This link no longer works after the recent, uhm, forum "improvements" cough. I'm stumbling over a similar problem… could you please advise?

Add a Comment

Thanks for pointing me to that thread.

So in case of using a dispatch source I will always need to resume it after cancelling ?

    dispatch_source_cancel(dispatchSource);
    dispatch_resume(dispatchSource);


This seems to work, it's just a little bit counter-intuitive to resume something that is being cancelled at the same time.

Is there a way to detect if a dispatch source is suspended ?

According to this there isn't.

I wonder if there's another way to have a timer that runs my method in a separate thread.

I wonder if there's another way to have a timer that runs my method in a separate thread.

There are ways to do that but it’s hard to say whether that’s a good idea or not because you haven’t really discussed the requirements of the DispatchTimer class, you’ve only talked about its internals.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

the link seems to be broken

Docs for this state:

Important: suspension applies to all aspects of the dispatch object life cycle, including the finalizer function and cancellation handler. Suspending an object causes it to be retained and resuming an object causes it to be released. Therefore it is important to balance calls to dispatch_suspend() and dispatch_resume() such that the dispatch object is fully resumed when the last reference is released. The result of releasing all references to a dispatch object while in an inactive or suspended state is undefined.

https://developer.apple.com/documentation/dispatch/1452801-dispatch_suspend

Unfortunately these docs aren't shown in Xcode on the protocol.


This link no longer works after

Unfortunately I’m unable to track down the thread I was referencing )-: [1]

Having said that, I think that robchefbrain is correct: The docs are clear that you can’t release your last reference to a suspended source.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] FYI, the migration from the previous DevForums platform to the current one did not lose any content. See this post for details. Moreover, in most cases old URLs continue to work, redirecting to the new location. There is, however, one case that’s still broken, namely URLs that point to the first post in a thread (as opposed to the thread itself) (r. 64589574).