1
votes

I've been experimenting with the core data setup recommended by Marcus Zarra's core data book. The setup entails two managed object contexts. A parent moc with concurrency type private. And a child main context. The stated reasoning behind this is that the main core data context can have super fast reads/writes since changes on the main context (on main queue) are propagated up to the parent, and not to disk.

However Zarra's core data initialization method sets up each context on the same thread. Since performblock* methods are executed on the same queue that the managedobjectcontext was created on, it sounds like all core data reads/writes are going to happen on the main queue. Wouldn't this stack be better served by setting up the private context on a background thread?

That thought led me to write code (inspired by code in Zarra's book) resembling the following:

    __block NSManagedObjectContext *private = nil;

    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSPersistentStoreCoordinator *psc = nil;
    psc = [self persistentStoreCoordinator];

    NSUInteger type = NSPrivateQueueConcurrencyType;
    private = [[NSManagedObjectContext alloc] initWithConcurrencyType:type];
    [private setPersistentStoreCoordinator:psc];
    });

    NSManagedObjectContext *moc = nil;
    type = NSMainQueueConcurrencyType;
    moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:type];
    [moc setParentContext:private];

    _mainManagedObjectContext = moc;
    _backgroundObjectContext = private;

   ...

When I set up my core data stack like this, I end up with a deadlock coming from performBlockAndWait as the main thread waits for itself to free up in order to execute that block of work...ad infinitum. Strangely, the queue i am attempting to perform work on is not just any global queue--it's the main thread. For some reason, dispatch_sync with one of the built-in global queues (or for a dispatch_queue i create myself) does not guarantee that the thread used for the chosen block of work will be a thread other than the main thread. Is there anyway other than going lower than gcd (e.g. using nsthread etc...) to guarantee that the block will be executed on a thread other than the main thread.

2

2 Answers

0
votes

Since performblock* methods are executed on the same queue that the managedobjectcontext was created on,

That statement is just plain wrong.

The only way to use performBlock is with a private queue or the main queue, and each call to performBlock will enqueue the block on the appropriate queue, regardless of which thread/queue was involved in the creation of the MOC.

Now, your deadlock is caused by using performBlockAndWait which has different behavior than performBlock. performBlockAndWait will cause the calling thread to wait until the block can be executed synchronously with respect to the MOCs dispatch queue.

Also, there is no guarantee at all on which thread certain blocks of code will run... except in the case of the "main" queue.

Finally, one should rarely, if ever use performBlockAndWait. Yes, it is re-entrant, but it can also cause deadlocks. It should be used only in specific cases where you know for certain that the code being called can not possibly be called from any other block. Those should be very rare, if your code is asynchronous, which it should be.

0
votes

You seem to be confused about which dispatch function you're using. In your code it's "_sync", but in the prose it's "_async". The purpose of dispatch_sync is to block the current thread until something completes, so this would be expected for how you wrote it. Try dispatch_async instead.