1

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];

Community
  • 1
  • 1
Peter Warbo
  • 11,136
  • 14
  • 98
  • 193
  • 1
    Why you are not performing fetches in the background thread? – Rajan Balana Mar 11 '13 at 10:02
  • @RajanBalana no reason...will running the fetches in a background thread help? won't they deadlock anyhow? – Peter Warbo Mar 11 '13 at 10:16
  • "Use a background thread" isn't a magic fix. BG threads might be appropriate, given the Core Data usage, but we can't say that given what we know currently. – occulus Mar 11 '13 at 11:09

2 Answers2

5

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

Community
  • 1
  • 1
occulus
  • 16,959
  • 6
  • 53
  • 76
  • 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];` – Peter Warbo Mar 11 '13 at 12:38
  • Hi Peter, add your clarification to your question where it's most noticable, not as a comment! – occulus Mar 11 '13 at 12:39
  • I just tried putting my fetch request inside a `@synchronized` block (http://stackoverflow.com/questions/11630702/coredata-application-freezes-while-performing-a-fetch-request-pthread-mutex-lock) 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];` – Peter Warbo Mar 11 '13 at 13:09
  • Again, please put extra info in the question, not the comments! – occulus Mar 11 '13 at 13:32
  • If @synchronised is making any difference, it looks like you're calling your fetchEntity: method from different threads. – occulus Mar 11 '13 at 13:32
  • Ah I noticed that I was actually calling the fetch from a background thread.. so if there are 2 calls to the same fetch and the calls are made on the same thread then @synchronized is not necessary? – Peter Warbo Mar 11 '13 at 13:53
  • If you were calling fetch from the background thread, yes this will fail, this is what I've been saying. It all has to be from the main thread given that this is how you've set up your managed object context. See part of my answer beginning 'update'. – occulus Mar 11 '13 at 16:56
  • So make sure you're calling your fetch from the main thread at all times, then you can remove the @synchronised. Does that fix your problem? If so, please consider accepting my answer. – occulus Mar 11 '13 at 16:56
0

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

Arjay Waran
  • 433
  • 5
  • 9