Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data Concurrency Debugging: False Positive

As mentioned in WWDC 2014 session 225 (Whatʼs New in Core Data), Core Data on iOS 8 and OS X Yosemite now support the command line argument -com.apple.CoreData.ConcurrencyDebug 1 to enable assertions that detect violations of Core Dataʼs concurrency contract.

In my experiments with this, I have found that it works under iOS 8 beta 1 (both on the device and in the simulator), but I seem to have found a false positive, i.e. the framework is throwing a multithreading violation exception where it should not do so. At least that's what I believe.

Question: is the code below correct or am I doing something that violates Core Dataʼs threading model?

What I do is set up a very simple Core Data stack (with an in-memory store, for simplicity's sake) with a managed object context named backgroundContext with private queue concurrency. I then invoke performBlockAndWait { } on that context and in the block I create a new managed object, insert it into the context, and save.

The save operation is where I get the multithreading violation exception from Core Data.

var backgroundContext: NSManagedObjectContext?

func setupCoreDataStackAndViolateThreadingContract()
{
    let objectModelURL = NSBundle.mainBundle().URLForResource("CoreDataDebugging", withExtension: "momd")
    let objectModel: NSManagedObjectModel? = NSManagedObjectModel(contentsOfURL: objectModelURL)
    assert(objectModel)

    // Set up a simple in-memory Store (without error handling)
    let storeCoordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: objectModel)
    assert(storeCoordinator)
    let store: NSPersistentStore? = storeCoordinator!.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil)
    assert(store)

    // Set up a managed object context with private queue concurrency
    backgroundContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    assert(backgroundContext)
    backgroundContext!.persistentStoreCoordinator = storeCoordinator!

    // Work on the background context by using performBlock:
    // This should work but throws a multithreading violation exception on 
    // self.backgroundContext!.save(&potentialSaveError)
    backgroundContext!.performBlockAndWait {
        NSEntityDescription.insertNewObjectForEntityForName("Person", inManagedObjectContext: self.backgroundContext!) as NSManagedObject
        person.setValue("John Appleseed", forKey: "name")

        var potentialSaveError: NSError?
        // In the following line: EXC_BAD_INSTRUCTION in
        // `+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
        let didSave = self.backgroundContext!.save(&potentialSaveError)
        if (didSave) {
            println("Saving successful")
        } else {
            let saveError = potentialSaveError!
            println("Saving failed with error: \(saveError)")
        }
    }
}

I have tested essentially the same code in Objective-C and got the same result so I doubt it is a Swift problem.

Edit: If you want to run the code yourself, I have put a project on GitHub (requires Xcode 6/iOS 8 beta).

like image 211
Ole Begemann Avatar asked Jun 11 '14 08:06

Ole Begemann


1 Answers

Apple confirmed this as a bug. It has been fixed with Xcode 6 beta 4.

like image 131
Ole Begemann Avatar answered Oct 17 '22 22:10

Ole Begemann