Peter Warbo
Peter Warbo

Reputation: 11700

Core Data deadlock

I'm doing 2 Core Data fetches that when they are performed at around the same time they will cause a deadlock in the main thread causing my app to freeze.

The fetches are made on the main thread. One way to deal with it would be to ensure that the fetches are not made around the same time but I have no control of when the fetches will be made. Is there any other way to avoid this deadlock?

Stack-trace when pausing the app is:

#0  0x9a5d191a in __psynch_mutexwait ()
#1  0x9166713b in pthread_mutex_lock ()
#2  0x01e5d591 in -[_PFLock lock] ()
#3  0x01e5d56a in -[NSPersistentStoreCoordinator lock] ()
#4  0x01e720ee in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] ()
#5  0x01e70539 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#6  0x0007cd62 in -[CoreDataHelper fetchEntity:predicate:andSortDescriptors:inManagedObjectContext:] at /xxx/CoreDataHelper.m:150
#7  0x000075f6 in -[RemindersListViewController refreshReminders] at /xxx/RemindersListViewController.m:64
#8  0x000082f8 in -[RemindersListViewController refreshGUI] at /xxx/RemindersListViewController.m:187
#9  0x00003735 in __58-[AppDelegate setTabCountAndScheduleRemindersInBackground]_block_invoke_2 at /xxx/AppDelegate.m:114
#10 0x022dc53f in _dispatch_call_block_and_release ()
#11 0x022ee014 in _dispatch_client_callout ()
#12 0x022de7d5 in _dispatch_main_queue_callback_4CF ()
#13 0x0261aaf5 in __CFRunLoopRun ()
#14 0x02619f44 in CFRunLoopRunSpecific ()
#15 0x02619e1b in CFRunLoopRunInMode ()
#16 0x02fd77e3 in GSEventRunModal ()
#17 0x02fd7668 in GSEventRun ()
#18 0x00d9dffc in UIApplicationMain ()
#19 0x0000297d in main at /xxx/main.m:16

EDIT: The different parts accessing Core Data are trying to do a fetch request. They are both calling this method:

NSArray *reminders = [[CoreDataHelper sharedInstance] fetchEntity:APReminderEntity predicate:nil andSortDescriptors:[NSArray arrayWithObject:dateAscendingDescriptor] inManagedObjectContext:nil];

CoreDataHelper.m

- (NSArray *)fetchEntity:(NSString *)entity predicate:(NSPredicate *)predicate andSortDescriptors:(NSArray *)sortDescriptors inManagedObjectContext:(NSManagedObjectContext *)context {

    DLogName()

    if (context == nil) {

        // Use default MOC
        context = self.managedObjectContext;
    }

    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entity inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];

    if (predicate != nil) {

        [request setPredicate:predicate];
    }

    if (sortDescriptors != nil) {

        [request setSortDescriptors:sortDescriptors];
    }

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:request error:&error];

    if (entities == nil) {        

        DLog(@"There was an error fetching entity: %@ Error: %@", entity, [error userInfo]);

        entities = [NSArray array];
    }

    return entities;
}

EDIT 2: I just tried putting my fetch request inside a @synchronized block (as suggested in this post) and so far it seems to work out great... I am accessing my MOC on the correct thread.. the MOC is initialized as _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

Upvotes: 1

Views: 4747

Answers (2)

Arjay Waran
Arjay Waran

Reputation: 433

I had the same result but different problem. I had multithread an already existing coredata app. It was using nothing but main thread.

I was specifying which context to use based on if I was on main thread or not. Using something like dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND may put you on main thread (it's an apple optimization). This made me mix contexts objects and cause a deadlock

Upvotes: 0

occulus
occulus

Reputation: 17014

It sounds like the problem is in your application's threading/mutexes/logic (or lack thereof) around dependent parts of code that want to use Core Data around the same time.

An often-neglected rule of threading:

Using the threading constructs in a language or API doesn't necessarily mean your code is thread-safe!

Without seeing your code and logic, it's hard to say more. What are the different parts accessing Core Data trying to do?

As a general strategy, an approach to removing your deadlock might be to collect the offending Core Data accessing code into a controller object that manages just the core data access. It could be responsible for avoiding whatever deadlock condition your code is throwing up.

On general matters:

NSManagedObjectContext locks the NSPersistentStoreCoordinator when it uses it, so having just one PSC shouldn't be a problem.

Be sure you follow the rules for threading in Core Data. In particular, each NSManagedObjectContext can only be accessed from one thread -- namely, the thread that created it.

For more discussion, see for example:

Update

I'm pretty certain your problem is that you're accessing NSManagedObjectContext from the wrong thread. You must access a context from the same thread that created it. Please verify you're doing this correctly in your code.

Are you sure you are calling your fetchEntity: method from the same thread that created the NSManagedObjectContext? How is that context created, and when?

See this question and answer: NSManagedObjectContext Locked

Upvotes: 5

Related Questions