To answer your exact question of: "What else can I use or look at to examine why these are deadlocking or how I can prevent these deadlocks from occurring?", I can respond with only a couple of thoughts that are very general because the problem you are approaching is hard:
- Add NSLog statements from everywhere you believe locks may be occurring. I find that using
NSLog(@"%s ...YOUR DEBUG STATEMENT", __PRETTY_FUNCTION__)
can help you identify exactly which function is being called, and which queue it is being executed from.
If we move beyond the question of what tools are available (which is basically nothing useful), then I am left with some suggestions about how to debug the system you describe:
As a bit of background: I have built smooth scrolling UITableViews with 2000+ dynamically downloaded and displayed variable-height cells with images which contains absolutely no jitters or delays due to data processing or drawing. This system originally was designed using CoreData, but eventually we moved to straight SQLite to get around exactly the problems that you are encountering with multithreading and parallelism. I am not advocating the switch to SQLite -- it was a decision we made internally to improve speed and reduce inconsistency in the local database. I state this as context to my answer.
I would start by looking hard at your use of Grand Central Dispatch. If you use any dispatch_sync calls, then make sure that they cannot occur in chains that block the operating thread. I did this originally to ensure that multiple threads did not access one of the managed object contexts at the same time, and only discovered the problem after a couple of hours of debugging. These can sneak up on you because the dispatch_sync calls may be deep in functions called by other functions.
I did end up using a transactional system (very SQL-y) with dynamically created queues for individual queries/updates that would ensure that too many operations would not occur at once. I also had a completely different system that would use a separate serial Read queue for quick reads to the DB. This queue's MOC would be mirrored from the other MOCs when possible. This was heavy, and very slow comparatively. Because the system was largely processing on background threads, this was isolated from the user.
CoreData is generally very difficult to multithread. SQLite is a little simpler, though you have to build a lot of application-specific architecture around it to make it usable.
If you would like to post additional detail about your system, I may be able to help more specifically. I hope this has been helpful.