Ok, so I have an app that is running a fairly standard CoreData + MagicalRecord stack. It's got one of these:
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreAtURL(whisperStoreURL)
To perform operations on our CoreData model we do two different things.
- MagicalRecord.saveWithBlock/MagicalRecord.saveWithBlockAndWait calls from the main or any other thread
- An NSOperationQueue full of NSOperations that contain a single MagicalRecord.saveWithBlockAndWait call.
This all works pretty well, except for a large crash in production, stack trace below. It seems to occur as if I was violating the CoreData multithreading rules, except as far as I can tell, I'm not. Objects/Contexts are never passed between threads.
When CoreData concurrency debugging is turned on, on our local machines we sometimes get a multithreading violation (AllThatIsLeftToUsIsHonor) but it doesn't point to anything in particular as the cause. This may be related, or it may not.
I also tried plugging in https://github.com/GrahamDennis/GDCoreDataConcurrencyDebugging to see if it provided any more useful information about concurrency violations. It throws out a lot of errors about release being called on a different queue from the one the object was created on, but apparently this is a false positive.
I'm not even really sure what "statement is still active" means. Googling around it seems like it has something to do with multithreading problems, but I'm 99% sure we aren't breaking any of the rules.
I'm pretty sure having multiple MagicalRecord blocks doing stuff at the same time shouldn't be an issue. In my app, one NSOperation might be saving some stuff, while another block on some other thread is also saving some stuff, while the main thread itself might be writing to the default context and saving that using MR_saveToPersistentStoreAndWait.
I should mention that due to legacy code that was ported over to MagicalRecord recently, there are many calls to MR_saveToPersistentStoreAndWait on the main thread for some stuff that hasn't been moved to background threads yet. This has worked fine until a recent release upped the amount of MagicalRecord/CoreData stuff in the codebase significantly.
I'm running out of things to investigate other than pushing vague attempts to fix the problem to production. Any advice on how to properly diagnose where the multi-threading problem is would be appreciated.
The stack for the crash (in a background thread) looks like this:
Fatal Exception: NSInternalInconsistencyException
statement is still active
Thread : Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x1844d1900 __exceptionPreprocess
1 libobjc.A.dylib 0x183b3ff80 objc_exception_throw
2 CoreData 0x185f32fbc -[NSSQLiteStatement cachedSQLiteStatement]
3 CoreData 0x185f327e4 -[NSSQLiteConnection prepareSQLStatement:]
4 CoreData 0x185f43020 -[NSSQLChannel selectRowsWithStatement:]
5 CoreData 0x18604534c newFetchedRowsForFetchPlan_MT
6 CoreData 0x185f3a7e4 -[NSSQLCore objectsForFetchRequest:inContext:]
7 CoreData 0x185f3a218 -[NSSQLCore executeRequest:withContext:error:]
8 CoreData 0x186001910 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke
9 CoreData 0x186009d58 gutsOfBlockToNSPersistentStoreCoordinatorPerform
10 CoreData 0x185ffc5c8 _perform
11 CoreData 0x185f39e70 -[NSPersistentStoreCoordinator executeRequest:withContext:error:]
12 CoreData 0x185f38938 -[NSManagedObjectContext executeFetchRequest:error:]
13 CoreData 0x185fda318 -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
14 CoreData 0x185fdaa44 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke
15 CoreData 0x185fcc76c _perform
16 CoreData 0x185fda824 -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]
17 CoreData 0x185f38938 -[NSManagedObjectContext executeFetchRequest:error:]
18 whisper 0x1003bd5bc __67+[NSManagedObject(MagicalRecord) MR_executeFetchRequest:inContext:]_block_invoke (NSManagedObject+MagicalRecord.m:54)
19 CoreData 0x185fd1080 developerSubmittedBlockToNSManagedObjectContextPerform
20 CoreData 0x185fd0f48 -[NSManagedObjectContext performBlockAndWait:]
21 whisper 0x1003bd500 +[NSManagedObject(MagicalRecord) MR_executeFetchRequest:inContext:] (NSManagedObject+MagicalRecord.m:50)
22 whisper 0x1003bd794 +[NSManagedObject(MagicalRecord) MR_executeFetchRequestAndReturnFirstObject:inContext:] (NSManagedObject+MagicalRecord.m:77)
23 whisper 0x1003bba84 +[NSManagedObject(MagicalFinders) MR_findFirstByAttribute:withValue:inContext:] (NSManagedObject+MagicalFinders.m:107)
24 whisper 0x1003bbdfc +[NSManagedObject(MagicalFinders) MR_findFirstOrCreateByAttribute:withValue:inContext:] (NSManagedObject+MagicalFinders.m:150)
25 whisper 0x10023325c specialized FetchWhispersForFeedResponseProcessor.processWhispers([String : AnyObject]) -> () (FetchWhispersForFeedResponseProcessor.swift:107)
26 whisper 0x100232250 FetchWhispersForFeedResponseProcessor.processStatusCode200() -> () (FetchWhispersForFeedResponseProcessor.swift:76)
27 whisper 0x10023199c FetchWhispersForFeedResponseProcessor.processWithLocalContext(NSManagedObjectContext) -> () (FetchWhispersForFeedResponseProcessor.swift:22)
28 whisper 0x100222d20 CoreDataResponseProcessor.(main(CoreDataResponseProcessor) -> () -> ()).(closure #1) (CoreDataResponseProcessor.swift:15)
29 whisper 0x1003b6144 __47+[MagicalRecord(Actions) saveWithBlockAndWait:]_block_invoke (MagicalRecord+Actions.m:48)
30 CoreData 0x185fd1080 developerSubmittedBlockToNSManagedObjectContextPerform
31 libdispatch.dylib 0x183f255f0 _dispatch_client_callout
32 libdispatch.dylib 0x183f3089c _dispatch_barrier_sync_f_invoke
33 CoreData 0x185fd0f68 -[NSManagedObjectContext performBlockAndWait:]
34 whisper 0x1003b60ac +[MagicalRecord(Actions) saveWithBlockAndWait:] (MagicalRecord+Actions.m:44)
35 whisper 0x100222a58 CoreDataResponseProcessor.main() -> () (CoreDataResponseProcessor.swift:16)
36 whisper 0x100222b9c @objc CoreDataResponseProcessor.main() -> () (CoreDataResponseProcessor.swift)
37 Foundation 0x184dbb348 -[__NSOperationInternal _start:]
38 Foundation 0x184e7a728 __NSOQSchedule_f
39 libdispatch.dylib 0x183f255f0 _dispatch_client_callout
40 libdispatch.dylib 0x183f31634 _dispatch_queue_drain
41 libdispatch.dylib 0x183f290f4 _dispatch_queue_invoke
42 libdispatch.dylib 0x183f33504 _dispatch_root_queue_drain
43 libdispatch.dylib 0x183f33224 _dispatch_worker_thread3
44 libsystem_pthread.dylib 0x184139470 _pthread_wqthread
45 libsystem_pthread.dylib 0x184139020 start_wqthread