Reproducible Core Data Deadlock: FRC and sectionNameKeyPath (iOS 8 and iOS 9)
Originator: | michael.gorbach | ||
Number: | rdar://21440887 | Date Originated: | 6/18/15 |
Status: | Open | Resolved: | |
Product: | iOS SDK | Product Version: | iOS 8 and iOS 9 |
Classification: | Reproducible: |
Sample: http://cloud.mgorbach.name/181W272I2k2a Summary: We have found a reproducible CD deadlock that can occur when using a fetched results controller with a "complex" (i.e. either multi-level or "synthesized" (not in the database) sectionNameKeyPath). This occurs specifically with a stack that has a top-level background (private queue) writing context, where the main thread context is a child of this context. Changes to CD (object creations and updates) as well as fetch requests, are taking place sequentially on background "import" contexts (private queue contexts). These contexts are also children of the top-level private queue writing context. If, while these CD changes and fetch requests are taking place in the background, the main thread Fetched Results Controller is asked to perform it's initial fetch, a deadlock results. The deadlock appears to occur as follows: On the main thead: Fetched Results Controller executes fetchRequest Fetch Request execution locks access main thread MOC performs _computeSectionInfo:error: _computeSectionInfo:error: takes the main-thread MOC lock again (OK because it is re-entrant) _computeSectionInfo takes the PSC lock (via performBlockAndWait: on the PSC) _computeSectionInfo does its work calls newValuesForObjectWithID:withContext:error:, which appears to require a lock on the parent context (the top-level private queue context) newValuesForObjectWithID waits on the parent context lock On some background thread: A Fetch Request is being executed executeFetchRequest: takes a lock on the background import context executeFetchRequest:withContext:error takes a lock on parent context calls _parentObjectsForFetchRequest:inContext:error: calls executeFetchRequest: for root / parent writing context (private queue context) executeFetchRequest waits on the PSC lock to do its work In summary: The main thread, having acquired the PSC lock, waits for the lock on the root writer context The background thread, having acquired a lock on the root writer context, waits for the PSC lock This looks like a situation where the locks are acquired in different order, and therefore a deadlock results. Workaround: This issue appears to have to do specifically with whether an object is faulted in during the fetch request on the main thread. If the objects being retrieved are faults at the time, everything is OK. If they have already been faulted in, we get this deadlock. Resetting the objects immediately prior to calling fetch on the FRC appears to work around the issue. (see commented out code) Note: Regression from iOS 7. This sample code does not deadlock on iOS 7. Note: Deadlock still occurs on iOS 9. Steps to Reproduce: See description for the scenario. To reproduce, run attached sample project. Notice deadlock. Uncomment the "refresh / merge changes" code in the second FRC fetch. Notice that deadlock goes away. Expected Results: No deadlock in either case. Actual Results: Deadlock. Version: iOS 8 and iOS 9 Notes: Configuration: Simulator or Device, iOS 8 and iOS 9
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!