Core Data privateQueue performBlockAndWait deadlock while accessing relationship Core Data privateQueue performBlockAndWait deadlock while accessing relationship multithreading multithreading

Core Data privateQueue performBlockAndWait deadlock while accessing relationship


  1. Standard messages is old Objective-C lingo. That means you should do all of the regular method calls on a ManagedObjectContext and its child ManagedObjects in the performBlock or performBlockAndWait. The only calls that are allowed on a private context outside of the block is init and setParentContext. Anything else should be done in a block.

  2. No. Any managed object fetched from a private context must only be accessed on that private context's queue. Accessing (read or write) from another queue is violating the thread confinement rules.

  3. The reason you are having blocking issues is because you have two levels of "mainQueue" contexts and that is "outsmarting" the queue system. This is the flow:

    • You create a context on the main queue and then create it as a child of another main queue context.
    • You create a private child of that second tier main queue context
    • You access that private queue context in such a way that it is trying to fault in the objects that currently are already loaded on the main queue context.

Because of the two levels of main queue contexts it is causing a deadlock where normally the queue system would see the potential deadlock and avoid it.

You can test this by changing your mainContext variable to:

lazy var mainContext: NSManagedObjectContext = {    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate    return appDelegate!.managedObjectContext}

And your issue goes away because the queue system will see the block and avoid it. You can even see that happening by putting a break point inside of the performBlockAndWait() and see that you are still on the main queue.

In the end, there is no reason to have two levels of main queue contexts like that in a parent/child design. If anything, this is a good argument NOT to do that.

Update

I missed that you had altered the template code in the appDelegate and turned the overall context into a private one.

That pattern of having a main MOC per vc throws away a lot of the benefits of Core Data. While having a private at the top and a main MOC (that exists for the entire app, not just one VC) is a valid design it won't work if you are doing performBlockAndWait like this from the main queue.

I would not recommend ever using performBlockAndWait from the main queue as you are blocking the entire application. performBlockAndWait should only ever be used when calling TO the main queue (or perhaps one background to another background).


  1. What are the "standard" messages?

Any message sent to the managed object context, or any managed object. Note that the documentation continues to clarify...

There are two exceptions:* Setter methods on queue-based managed object contexts are thread-safe.  You can invoke these methods directly on any thread.* If your code is executing on the main thread, you can invoke methods on the  main queue style contexts directly instead of using the block based API.

Thus, anything but a setter method on a MOC must be called from inside a performBlock. Any method on a MOC that is of NSMainQueueConcurrencyType may be called from the main thread without being wrapped inside a performBlock.

  1. Can we set properties of a managed object which is fetched inside performBlock* API of a context outside performBlock*?

No. Any access of a managed object must be protected from inside performBlock on the managed object context in which the managed object resides. Note the exception for managed objects residing in a main-queue MOC being accessed from the main queue.

  1. Why performBlockAndWait is misbehaving and causing UI block in my test code.

It is not misbehaving. performBlockAndWait is reentrant, but only when already processing a performBlock[AndWait] call.

You should never use performBlockAndWait unless you have no other option. It is especially problematic with nested contexts.

Use performBlock instead.