Reputation: 33060
I have one managedObjectContext with concurency type of NSMainQueueConcurrencyType
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
@synchronized(self)
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
}
return __managedObjectContext;
}
The main managedObjectContext is NEVER accessed outside of the main thread except when setting the other managedObjectContext.parent. So that mainManagedObjectContext is the parent for all threads.
Now when I run the program, sometimes it reach a deadlock. I pause the program and this is what I see:
As we see in the picture, there are 2 threads that seems to be in the deadlock. The first is the main thread.
It deadlock on @synchronize (self). Reasonable.
The other thread is deadlock on:
So it locks when trying to change the persistent store of the static variable that hold the __managedObjectContext.
Let me put the code again to reiterate:
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
@synchronized(self) //Main thread deadlock here
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator]; //Secondary thread dead lock here
}
}
}
return __managedObjectContext;
}
My question is why in the earth the [__managedObjectContext setPersistentStoreCoordinator:coordinator];
NOTHING else is accessing __managedObjectContext. The second thread (the non main one) is trying to set the __managedObjectContext as the parent context. The first thread is just waiting happily in @synchronized. It doesn't do anything.
So why the deadlock and how to solve that?
Oh the child managedObjectContext is created here:
@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread]; //Stuck here. This goes straight to above function managedObjectContextMainThread where it stucks.
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
Upvotes: 1
Views: 1931
Reputation: 33060
The problem is I tried to create the main managed object context on a thread different than the main thread.
For some reason it doesn't work.
I still love lazy loading. So all I need to do is to ensure that the main managedObjectContext is created on
So that looks like a job for dispatch_sync.
I then added this code:
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
before I created all background child managedObjectContexts. That should be fast because once created the function will only return the static variable.
+(NSManagedObjectContext *)managedObjectContext {
NSThread *thread = [NSThread currentThread];
//BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
//NSManagedObjectContext *moc = delegate.managedObjectContext;
if ([thread isMainThread]) {
//NSManagedObjectContext *moc = [self managedObjectContextMainThread];
return [self managedObjectContextMainThread];
}
else{
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
}
// a key to cache the context for the given thread
NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;
@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator];
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
return [managedObjectContexts objectForKey:[self threadKey]];
}
Upvotes: 2