I am writing a function that performs some CoreData stuff. I want the function to return only after all the CoreData operations have executed. The CoreData stuff involves creating an object in a background context, then doing some more stuff in the parent context:
+ (void) myFunction
NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext;
[backgroundContext performBlockAndWait:^{
MyObject *bla = create_my_object_in:backgroundContext;
[backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil];
[backgroundContext save:nil];
[[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlockAndWait:^{
[[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone];
// Do some more stuff
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:someOperation];
}];
}];
return;
}
I want the return to only happen after [queue addOperation:someOperation].
This seems to work most of the cases, but I have had one case when this function never returned. It seemed like it was deadlocked, and I suspect it is because of performBlockAndWait.
My questions are:
(1) Can someone explain why this deadlock occurs?
and
(2) What is the right way of achieving the same functionality? The requirement is that myFunction returns only after both blocks have been executed.
Thank you!
Let’s imagine you are calling
myFunctionfrom the main thread. Let’s imagine[DatabaseDelegate sharedDelegate].parent.managedObjectContextis scheduled on the main thread.With
[backgroundContext performBlockAndWait:]you are scheduling a block on the context private background queue. Blocking the main thread.With
[.parent.managedObjectContext performBlockAndWait:], you are scheduling a block on the main thread, blocking the private queue.But the main thread is blocked already. So the block will never execute. And
performBlockAndWait:will never returns.Deadlock.
Use asynchronously scheduled blocks, with completion blocks.