Nested performBlock: on NSManagedObjectContext
Asked Answered
A

1

8

When using NSPrivateQueueConcurrencyType and NSMainQueueConcurrencyType types for NSManagedObjectContext, is it safe to make nested performBlock calls on the same context ?

[backgroundContext performBlock:^{
   NSFetchRequest *myRequest = ...;  
   __block NSArray *result= nil;
   [backgroundContext performBlockAndWait:^{
       results = [backgroundContext executeFetchRequest:myRequest error:NULL];
   }];
}];

It may seem stupid but I have an existing codebase with a lot of helpers methods which encapsulate the executeFetchRequest calls. I don't want to make assumptions about whether the caller has already used performBlock or not. For example:

-(void)updateObjects:(BOOL)synchronous
{
    if (YES == synchronous)
        [self fetchHelper];
    else
    {
        [backgroundContext performBlock:^{
             [self fetchHelper];
        }];
    }
}

-(NSArray*)fetchHelper
{
     [self.backgroundContext performBlockAndWait:^{
         //Fetch the objects...
         [self.backgroundContext executeFetchRequest: (...)];
     }];
}

I have tried it and it works. But I have learned (the hard way) to be very careful with Core Data and multi-threading.

Andie answered 4/5, 2012 at 8:22 Comment(0)
D
9

Yes, performBlockAndWait is reentrant. Directly from Apple's release notes...

Core Data formalizes the concurrency model for the NSManagedObjectContext class with new options. When you create a context, you can specify the concurrency pattern to use with it: thread confinement, a private dispatch queue, or the main dispatch queue. The NSConfinementConcurrencyType option provides the same behavior that was present on versions of iOS prior to 5.0 and is the default. When sending messages to a context created with a queue association, you must use the performBlock: or performBlockAndWait: method if your code is not already executing on that queue (for the main queue type) or within the scope of a performBlock... invocation (for the private queue type). Within the blocks passed to those methods, you can use the methods of NSManagedObjectContext freely. The performBlockAndWait: method supports API reentrancy. The performBlock: method includes an autorelease pool and calls the processPendingChanges method upon completion.

Dorthea answered 8/5, 2012 at 15:20 Comment(9)
What about performBlock, is that reentrant too?Recuperator
It is not, this is covered in the session. Your requests will queue up if you call performBlock as it is async.Allness
just to be clear, what the OP is doing in the second bit of code is ok to do?, but it could cause problems if both methods had "performBlock"? is that the correct way to look at this?Pursuivant
That depends on your definition of OK. I would not do that. My rule of thumb is that use of performBlockAndWait is for exceptional use cases only, and I make every attempt to not use it. It's a deadlock just waiting to happen (you must know the context in which it is called or you could deadlock), and it violates the FIFO principle (nested calls execute now and "jump the queue" of waiting async blocks). This is even more true with the new async fetching in iOS8.Dorthea
@JodyHagins. A small note on performBlockAndWait and FIFO principles. When a caller calls performBlockAndWait on a CoreData with a Queue, the queue does not put the new block in front of all the other Async queued items. The calling thread just waits for its block to execute and then return. By then all other Async blocks have executed. When it comes to nested performBlockAndWait, the same principles would deadlock. (It is like calling dispatch_sync on the main thread while you are running on the main thread). But CoreData's performBlockAndWait has been made re-entrant to not deadlock.Pushcart
@KrisSubramanian - maybe that's how it is now - but I assure you that's now how it was then, or just a year or so ago. If I can get some time (which is not likely) I'll run an experiment with the latest libraries - or if anyone else wants to run the test, that would be great as well.Dorthea
Can I call performBlock inside another performBlock on different contexts?Tychonn
@Tychonn Yes, it will schedule another asynchronous task.Dorthea
Is it safe to call performBlockAndWait inside another performBlockAndWait ? If i do delete operation in performBlockAndWait and context save (both child and parent) in performBlock, is that going to create any issue ?Transect

© 2022 - 2024 — McMap. All rights reserved.