views:

71

answers:

1

I have an NSOperation which fetches some objects from a core data persistent store and sums up a few totals. Sometimes an object is deleted while the operation in in progress, so a core data fault exception occurs. I try/catch the exception while summing to ignore it because I just want to skip objects that cannot be faulted in.

However, when one of these fault exceptions occurs (and I swallow it) there is a crash after the invocation returns in [NSInvocation invoke]. It's a bad memory access when dereferencing the value in r10 which according to GDB on a successful run points to one of these:

(gdb) x 0x38388348
0x38388348 <OBJC_IVAR_$_NSInvocation._retdata>:     0x00000008

If a fault exception occured a value of 0x02 is in the register which causes the crash.

A quick google search tells me that r10 should be saved by the callee, meaning it is not being restored by whatever code is changing it when this exception occurs.

Can anybody explain this? I'm not an expert when it comes to these kinds of low-level details

A: 

I think you're asking the wrong question. A Cocoa application should never throw an exception under normal operation. You need to address the cause of that exception before doing anything else.

You should not be accessing a single managed object context from multiple threads, or if you are you need to lock it very carefully. Multithreaded access to one context is highly dangerous, and can lead to the kind of behavior you are observing. From the "Multi-Threading with Core Data" section of the Core Data Programming Guide:

If you want to work with a managed object across different threads, you must lock its context (see NSLocking). If you try to pass actual objects, share contexts between threads, and so on, you must be extremely careful about locking (and as a consequence you are likely to negate any benefit you may otherwise derive from multi-threading). Working with a managed object across different threads is therefore strongly discouraged, as described in “General Guidelines.”

It is recommended that you use a separate managed object context for your background thread (with the same persistent store and managed object model), and do all of the reads you need for your summation operation there.

Brad Larson
I do use a separate context, and the only workaround to fault exceptions is to check if the objectID exists before trying to access members. But even this isn't perfect.
Mike Weller
@Mike Weller - If I understand you correctly, you've created a separate managed object context for your background operation, but you're getting exceptions when you try to fault in certain managed objects from the persistent store because they've been deleted in a separate context which has written those changes to disk. In that case, you'll need to make sure that your background context reconciles changes, as described here: http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/CoreData/Articles/cdChangeManagement.html#//apple_ref/doc/uid/TP30001201-CJBDBHCB
Brad Larson
I'm familiar with conflict resolution. The problem is that when the operation fetches the objects they obviously all exist. There are lots of objects and iterating them and calculating the information takes a long time.Being able to detect a delete from the other context and somehow remove references to deleted objects just isn't possible in the operation. My current workaround is simply to check the result of [context existingObjectWithID:] before accessing members on the object. If the item has been deleted, this returns nil. Theoretically this is "race-y" but it should be OK in practice.
Mike Weller