Crash with dispatch_block - ios

I have been trying to understand the reason behind this crash for the sake of understanding more about how blocks behave. I have a really simple class to trigger this crash.
#implementation BlockCrashTest
- (void)doSomething
{
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_group_t group = dispatch_group_create();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_group_enter(group);
[strongSelf performSomethingAsync:^{
dispatch_group_leave(group);
}];
if(dispatch_group_wait(group, time) != 0) {
NSLog(#"group already finished");
}
};
dispatch_async(queue, block);
}
- (void)performSomethingAsync:(void(^)(void))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
completion();
});
}
- (void)dealloc
{
NSLog(#"released object");
}
#end
Now, if I allocate the class and simply call method doSomething to it,
BlockCrashTest *someObject = [[BlockCrashTest alloc] init];
[someObject doSomething];
It crashes with the exception, EXC_BAD_INSTRUCTION and following stack traces,
#0 0x000000011201119a in _dispatch_semaphore_dispose ()
#1 0x0000000112013076 in _dispatch_dispose ()
#2 0x0000000112026172 in -[OS_dispatch_object _xref_dispose] ()
#3 0x000000010ef4c2fd in __29-[BlockCrashTest doSomething]_block_invoke at /Users/Sandeep/Desktop/Test Block Crash/Test Block Crash/ViewController.m:35
#4 0x0000000112005ef9 in _dispatch_call_block_and_release ()
If I modify the method doSomething, such that it do not use weak but uses self then the crash do not occur and the methods seem to execute as expected,
- (void)doSomething
{
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = ^{
dispatch_group_t group = dispatch_group_create();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_group_enter(group);
[self performSomethingAsync:^{
dispatch_group_leave(group);
}];
if(dispatch_group_wait(group, time) != 0) {
NSLog(#"group already finished");
}
};
dispatch_async(queue, block);
}
Why does it crash, my understanding is that using weak inside the block would make sure that the method would not be called, if the object is released and I thought that weak is safer than using self inside the block.
The above code works fine with weakSelf, if I retain the object BlockCrashTest and call the method to it.
I would be really happy if someone could explain the reason behind the crash and what exactly happens with these 3 different variants of code above that one crash and other seem to work fine.
Note: This is in one way or other related to the crash listed in
thread,
Objective-C crash on __destroy_helper_block_.
I have been able to reproduce the exact same stack traces with my code
above.

A couple of observations:
You cannot have a dispatch group with unbalanced "enter" and "leave" when the dispatch_group_t object is deallocated. And as ilya pointed out, because of your pattern, strongSelf is nil, so you're entering the group, but not leaving it.
A very common pattern in the weakSelf and strongSelf dance is to just check to see if strongSelf was nil or not, resolving the imbalance. Thus, if strongSelf is nil, it bypasses the dispatch group stuff altogether, but if it is not nil, both "enter" and "leave" will be called:
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[strongSelf performSomethingAsync:^{
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
});
}
Clearly, you must make sure that performSomethingAsync method, itself, always calls the block which leaves the group.
The other way of solving this (if you don't have assurances that all of the "enter" and "leave" will be balanced), is to use semaphores:
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[strongSelf performSomethingAsync:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, time) != 0) {
NSLog(#"semaphore not received in time");
}
});
}
Frankly, even when using semaphores, like I have above, I still think one would want to check to confirm that strongSelf was not nil. Concurrent programming is confusing enough without adding the possibility of the message to a nil object resulting in an no-op.
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[strongSelf performSomethingAsync:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, time) != 0) {
NSLog(#"semaphore not received in time");
}
}
});
}

You'll get the same crash even if you remove call of self performSomethingAsync:. This crash caused by libdispatch semaphore API. You can see assembly trace of crashed function _dispatch_semaphore_dispose in Xcode:
If we try to figure out what happens in this code we'll see that you explicitly mark current block entered the group by calling dispatch_group_enter. Then performSomethingAsync doesn't call because strongSelf == nil. Which means that dispatch_group_enter is not balanced with dispatch_group_leave this cause the group couldn't dispose properly and crashed (see asm listing).
If you use self this code also crashed because dispatch_group_leave(group); called from the different thread with dispatch_group_enter Which is also cause the same crash but in another perspective: calls not balanced in the same thread. performSomethingAsync called completion block in different queue not in yours "com.queue.test".
This example is just wrong using of dispatch_groups APIs. To see how properly use it see apple doc.

MacOS 10.8 and iOS 6.0 introduced ARC to dispatch objects. From the documentation GCD Objects and Automatic Reference Counting:
When you build your app using the Objective-C compiler, all dispatch objects are Objective-C objects. As such, when automatic reference counting (ARC) is enabled, dispatch objects are retained and released automatically just like any other Objective-C object. When ARC is not enabled, use the dispatch_retain and dispatch_release functions (or Objective-C semantics) to retain and release your dispatch objects. You cannot use the Core Foundation retain/release functions.
If you need to use retain/release semantics in an ARC-enabled app with a later deployment target (for maintaining compatibility with existing code), you can disable Objective-C-based dispatch objects by adding -DOS_OBJECT_USE_OBJC=0 to your compiler flags.
In your case ARC is happily managing the life cycle of your dispatch_group_t. And, unfortunately, your code is causing the group to be released while the lock is still waiting. When the group times out it is released - so when dispatch_group_leave is called it crashes, as the group has already been released.
I would suggest at the very least checking wether the group is NULL before attempting to leave it.
Additionally, your wait result logic is reversed. A zero result indicates the group was emptied before the timeout, a non zero result indicates the timeout was hit.

Related

Yin&Yang Initialization - Wait for pieces before calling init

Say you have a method that returns information in two separate blocks, like so:
#interface SomeObject : NSObject
- (instancetype)initWithA:(NSString *)aInfo bInfo:(NSString *)bInfo;
#end
- (void)someMethod:(void (^)(NSString *aInfo))firstBlock
secondBlock:(void (^)(NSString *bInfo))secondBlock {
firstBlock(#"a"); secondBlock(#"b");
}
- (void)ourMethod:(void (^)(SomeObject *object))completionBlock {
SomeObject *someObject = [[SomeObject alloc] initWithA:aInfo bInfo:bInfo];
[self someMethod:^(NSString *aInfo) {
//
} secondBlock:^(NSString *bInfo) {
//
}];
completionBlock(someObject);
}
How would you initialize someObject and pass it back when both of the blocks have completed?
Assume that both blocks are executed asynchronously.
I tried fiddling with GCD's dispatch groups to solve this, however, it didn't seem optimal.
Since you need to create your someObject with the values obtained from the two blocks used in the call to someMethod, you need to create someObject after both blocks have been called.
- (void)ourMethod:(void (^)(BOOL initializationComplete))completionBlock {
__block NSString *a = nil;
__block NSString *b = nil;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_enter(group);
[self someMethod:^(NSString *aInfo) {
a = aInfo;
dispatch_group_leave(group);
} secondBlock:^(NSString *bInfo) {
b = bInfo;
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
SomeObject *someObject = [[SomeObject alloc] initWithA:a bInfo:b];
completionBlock(someObject);
});
}
This doesn't block the caller of ourMethod and it ensures the completion block is only called once both blocks are done.
This solution assumes the two blocks are run asynchronously.
You can use a semaphore, but-- in general-- making an asynchronous operation synchronous is a red flag indicating bad design.
Are the two blocks asynchronous in and of themselves? If so, you could have __block BOOL firstDone = NO; and __block BOOL secondDone = NO; and check appropriately to see if it is time to call the completionBlock. Still ugly and you'll want a synchronization primitive in there to ensure you don't hit a race, but that'd work.
If firstBlock() and secondBlock() are synchronous and on the same queue, then just call completionBlock() after the second is done.
Or, alternatively, if they are asynchronous and simultaneously scheduled, toss 'em on an asynchronous queue and then toss a barrier block on the queue that calls the completionBlock.

Background Sync in iOS

I have a Sync class which basically syncs the data to server in background.
The use of Sync class object is such that the View Controller from where I make a sync request on the Sync class object may get deallocated before the sync gets actually complete.
The problem I am facing here is the Sync class object gets deallocates too and hence the sync request never gets complete.
I thought of making Sync class as a Singleton but that would be more of abuse of the Singleton pattern.
How should I proceed?
You could keep strong pointer to the object before starting operation:
__block StubClass *strongSelf = self;
And start method on it:
[strongSelf dataSync: ^... { //completionBlock
strongSelf = nil;
}];
Or do it with semaphore:
__block StubClass *strongSelf = self;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[strongSelf dataSync: ^... {
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
strongSelf = nil;

Usage of dispatch group inside another group

Needed some advice/review on possible downsides of using a dispatch group inside another group, if it could lead to a race condition/deadlock or just wrong practice.
1) Can a dispatch_group_enter exist inside the scope of another group? I could not find an example from Apple following such practice. Remember, secondCall needs to happen after firstCall. There is a dependency. Thoughts?
2) What would be a good design to execute a thirdCall - which again depends on result of firstCall result. But agnostic of the completionHandler timing i.e. can happen later and doesn't need to wait for completionHandler to finish.
Here's a simplified example of the completion handler incorporating 3 calls -
-(void)someMethod:(void (^)(NSError *error))completionHandler {
dispatch_group_t serviceGroup = dispatch_group_create();
dispatch_group_enter(serviceGroup);
__typeof__(self) __weak weakSelf = self;
[self.obj firstCall completion:^(NSError *firstError) {
__typeof__(self) strongSelf = weakSelf;
// Second Call
if (!firstError.code) {
dispatch_group_enter(serviceGroup);
[strongSelf.obj secondCall completion:^(void) {
dispatch_group_leave(serviceGroup);
}];
}
// Third call
if (!firstError.code) {
[strongSelf executeThirdCall];
}
dispatch_group_leave(serviceGroup);
}]; // Closing block for first call.
dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
if (completionHandler) {
completionHandler(error);
}
});
}
Some classic examples of dispatch groups can be found in this answer.
I can't think of any issues with this code.
However I am not sure you need dispatch groups at all for this example.
You are executing three requests. Request 2 and Request 3 both depend on the result of the Request 1. You need to call the function's completionHandler when Request 2 is finished. Can't you do it into the completion handler of Request 2?

ObjectiveC waiting on for loop blocks with semaphore

I have to run a method with block, several times inside a for loop.
I also have to wait until all the blocks execution completes.
My problem is that I can't understand what I do wrong, that causes my entire app to freeze. Here is the code:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//1 - creating semaphore
for(int i = 0; i< myObj.count; i++){
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
dispatch_semaphore_signal(semaphore);//3 - signaling semaphore to continue
}
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//2 - getting semaphore to wait
}
//I want to continue once all DB checks complete
Now, I don't understand, why the semaphore won't release, and the for loop won't continue.
What I actually need, is for the semaphore to release after all the DB checks complete. Ideally, I would want the semaphore to wait outside the for loop. Any suggestions on how to accomplish this?
EDIT: SOLUTION: (based on the accepted answer)
// create a group
dispatch_group_t group = dispatch_group_create();
for(int i = 0; i< myObj.count; i++){
// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
}
dispatch_group_leave(group); //1 leave
}];
//Get a notification on a block that will be scheduled on the specified queue
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"-all done!-");
//code here
});
}
Without access to verifyObjectid:completiongBlock:, there are a couple of issues. First, you only call dispatch_semaphore_signal if found is true. If found is every false, you'll deadlock. That may just be a transcription error and your real code might not do that.
Another guess is that the completion block is being submitted to the queue that you're currently running on (the main queue?) If that's true, then that would definitely be a deadlock, because you'll never run dispatch_semaphore_signal since it's waiting on dispatch_semaphore_wait. I can't tell without information about DataManager.
Your approach also serializes the calls, whereas I think you wanted them to be in parallel. Each call has to wait for the former one to finish in your code.
The better tools to use here are dispatch_apply and dispatch_group. Something like this (untested):
dispatch_group_t group = dispatch_group_create();
dispatch_apply(myObj.count, dispatch_get_global_queue(0, 0), ^(size_t i){
dispatch_group_enter(group);
[[DataManager shared] verifyObjectId:myObj[i].id
completionBlock:^(BOOL found) {
if(found){
//code here
}
dispatch_group_leave(group));
}];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_apply won't return until all the blocks have completed running, which means that dispatch_group_enter has run "count" times. You then use dispatch_group_wait to wait for all the calls to dispatch_group_leave.

How to timeout an asynchronous method when ran synchronously

This is essentially what I'm doing to run an asynchronous method synchronously:
This essentially works when called once, but when called multiple times, it will eventually stay inside the while loop and never get signaled. Any ideas on how to set a timer to eventually time out after sometime?
__block SomeClass *result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
dispatch_semaphore_signal(semaphore);
}];
});
// wait with a time limit
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
dispatch_release(semaphore);
Thanks
That looks kind of like GCD abuse to me. ;) Are you running the run loop because this is executing on the main thread? Why not just use a dispatch_async() from your completion handler to invoke a handler on the main thread? eg:
- (void)handleDataReady: (id) results error: (NSError *) error {
// update your app
}
- (void)performAsyncUpdate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDataReady:responseObject error:error];
}];
});
}
If you really want to make it synchronous, i.e. blocking the calling thread until the operation completes then use the following pattern (of course you want to avoid blocking threads if possible)
NSCondition *waitCondtion = [NSCondition new];
__block BOOL completed = NO;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
[waitCondtion lock];
completed = YES;
[waitCondition signal];
[waitCondition unlock];
}];
});
[waitCondtion lock];
if (!completed)
[waitCondtion wait];
[waitCondition unlock];
You can also use "waitUntilDate:" to timeout the wait after a period.
However, this pattern only works as long as the "someMethodWithCallback does not call its callback block on the same thread that is being blocked. I have copied your code because it is not obvious how "someMethodWithCallback" is implemented. Since this method is using an asynchronous pattern, then it must be doing something asynchronously therefore why are you calling it inside a dispatch_async? What thread will it call its callback block on?
You should "fill" the completion handler with whatever code you require to process the result when the completion handler finished (and also completely removing that run loop).
In order to "abort" an asynchronous operation, you should provide a cancel message which you send the asynchronous result provider.
In your case, since you have a singleton, the cancel message would have to be send like this:
[[SomeManager sharedInstance] cancel];
When the operation receives the cancel message, it should as soon as possible abort its task and call the completion handler with an appropriate NSError object indicating that it has been cancelled.
Note, that cancel messages may be asynchronous - that means, when it returns, the receiver may still execute the task.
You may achieve a "timeout" with setting up a timer, which sends the cancel message the operation, unless it has been invalidated when the operation finished.

Resources