Recently when I was doing some performance fixes for an iOS app I am working on, I came across a strange issue.  The app UI would freeze indefinitely without causing a crash or hitting a breakpoint. This freeze was intermittent, which made things difficult. Initially I suspected a deadlock in some new code, but I had not introduced any explicit @synchronized or semaphore wait statements in the code <which generally cause deadlocks>. All I had done is moved JSON response parsing code to a shared background queue.

On the debugger, hitting the pause button showed me that the execution was stuck in [pmutex wait] method. Quick googling of this gave me the answer I was looking for.

core data is not thread safe

core data is not thread safe !


What had happened was that I was using Core Data abstraction for database storage and I had made a singleton class for all database methods (seems like a bad idea now). For this singleton, I was using a single instance persistentStoreCoordinator and a single instance of managedObjectContext. As long as all access to the database was on main thread, this would work beautifully.  But as soon as I tried to execute a fetch request or to perform a save operation, the UI thread would go into a deadlock.

This is because Core Data operations are not thread safe. Apple recommends every thread to have its own managedObjectContext or have a child context of the main context. What they also recommend is that if you perform any save operation on a non main thread, you have to raise a NSNotification asking the main context to merge the changes.


So what I ended up doing was somewhat slightly different. Instead of keeping a common managedObjectContext across all methods inside a thread, I used a temporary context inside the method which was executing. Thats because creating and discarding contexts is a light operation and its much easier to manage.

For e.g in my singleton class, if I have a method called fetchAccounts which previously used self.managedObjectContext (created for the first time from main thread), I modified fetchAccounts to do this :

 NSManagedObjectContext * context = [[NSManagedObjectContext alloc] init];

[context setPersistentStoreCoordinator:self.persistentStoreCoordinator];

< then use this context in any fetch request or save operation >

Here, the persistentStoreCoordinator need not created and we use the same instance shared across all threads.

This made it easier to manage even for the future because we dont have to worry whether any change in the codebase will accidentally move execution of some code to a new thread or dispatch queue. 

But creating a new context each time a fetch request is created comes with a overhead, be it a small one. So for core data methods which get called too frequently, we have to share the same context.

This changed fixed all the deadlocks in the code and made the UI generally smooth. I wish apple could raise exceptions whenever such a deadlock is about to happen. Makes debugging a lot easier on iOS.

Incoming search terms: