How do I resolve this deadlock that happen ocassionally?
Asked Answered
H

1

1

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:

enter image description here

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:

enter image description here

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]];
    }
}
Hauge answered 11/10, 2012 at 9:24 Comment(0)
H
2

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

  1. the main thread
  2. before any other managedObjectContexts are created.
  3. I do not want to ensure that my program do not try to access the other managedObjectContexts first

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]];
}
Hauge answered 12/10, 2012 at 0:47 Comment(1)
Just a simple remark: Creating managed object contexts is a very cheap operation. It really is! So lazy creation of the context is not needed really and only adds additional complexity, more code to write and you gain more or less nothing. Lazy creation/loading should only be applied where it really makes sense. If you create the context lazily why not everything else as well? Strings, numbers, small arrays could all be created lazily but this is not done to keep it simple.Errecart

© 2022 - 2024 — McMap. All rights reserved.