Coredata Asynchronous Fetch Causes Concurrency Debugger Error

Asynchronous read in CoreData - Difference in using newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest?

1. About the __Multithreading_Violation_AllThatIsLeftToUsIsHonor__ exception:

There is a detailed discussion in this thread:

CoreData asynchronous fetch causes concurrency debugger error

The consensus is that it is a bug in CoreData.

There is a bug report: https://openradar.appspot.com/30692722 which is still open after 8 years at the time of writing.

2. How to properly use NSAsynchronousFetchRequest

The API was introduced in 2014 and was discussed in this WWDC video 225_sd_whats_new_in_core_data.

It is not mentioned whether NSAsynchronousFetchRequest should be used on the main (view) context or on a background context.

I looked over a couple of random implementations using NSAsynchronousFetchRequest on GitHub and I found examples for both main and background context.

One thing you must do when using a background context however, is wrapping the fetch execution in in a perform block (documentation).

In the article you linked and in the example that you excerpted above, this is missing!

This is how it should look like:

privateManagedObjectContext.perform {
do {
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("error trying to fetch saving objects:", error.localizedDescription)
}
}

There is another potential issue with the same article, so take it with a grain of salt:

DispatchQueue.main.async {
// here the objects in result (belongs to private context) are
// accessed on the main queue – the whole point is to *not* do that!
// one might get away with it because it is only read access to id
// but good luck debugging this...
let dogs: [Dog] = result.lazy
.flatMap { $0.objectID }
.flatMap { mainManagedObjectContext.object(with: $0) as? Dog }
// ...

The way I understand NSAsynchronousFetchRequest is that it is best used from the main context and that its purpose is actually to hide the background context business from you.

So: mainContext + NSAsynchronousFetchRequest

3. How should I choose one over another?

It seems to me that NSAsynchronousFetchRequest was created with good intention to simplify asynchronous core data fetching for us. And to that end you may well use it to your advantage, especially if you need to deal with progress and cancellation.

However I probably would not use it in my project because

  • the documentation is sparse
  • it is not well maintained (open bugs since forever)
  • it is not well adopted (e.g. the popular and excellent CoreData wrapper CoreStore does not use it)

One last thought - before going into asynchronous fetching at all make sure that you really need it. It may be better to optimise the performance of your query, data model or batch settings first.

Why does the raywenderlich code break if you enable the scheme -com. apple.CoreData.ConcurrencyDebug 1

This is a good catch. I wish the answer helped more though.

I downloaded the code and tried it out, and after messing with it for a bit I'm pretty sure that this is a bug in Apple's frameworks. It looks like the code in the sample project is correct, but the concurrency check flags it anyway. I don't know if this is because the code actually causes a concurrency violation or if there's no violation but the flag is buggy.

What you're seeing is unfortunately just what happens right now, and it's not fixable in an app-- only Apple can resolve it. It's also a longstanding issue, as you can see from this answer from a few years ago. I suggest filing a bug with Apple.

Correctly accessing Core Data from another thread using the new Asynchronous Task in Swift

I fixed some of these issues by adding the @MainActor flag to the Task

    Task { @MainActor in
await reloadForecasts()
}

However I was still getting breakpoints for certain issues, especially higher order functions like maps or sorts. I ended up adding the @MainActor wrapper to all my view models.

This fixed all of the weird crashes regarding the higher order functions accessing the core data objects, but I faced new problems. My second core data context used for saving objects was now the cause of concurrency breakpoints being triggered.

This made more sense and was much more debug-able. I had strong references to objects fetched in the main context, used to construct another object in the background context.

Model A <---> Model B

I had Model A that had relationship to another Model B. To setup the relationship to Model B, I used a reference to a Model B object that had been fetched on the main context. But I was creating the Model A object in the background thread.

To solve this I used the suggested methods of refetching the required objects by ObjectID in the correct context. (Using a bunch of nice helper methods to make things easier)

Here's a forum post asking a related question about ensuring asynchronous tasks are run on the main thread

https://forums.swift.org/t/best-way-to-run-an-anonymous-function-on-the-main-actor/50083

My understanding of the new swift concurrency models is that when you await on an async function, the task is run on another thread chosen from a pool and when the task is complete, execution returns to the point (and thread) you used await.

In this case I have forced the Task to start on the main thread (By using @MainActor), execute its task on a thread from the available pool, and return back to the main thread once its completed.

The swift concurrency explains some of this in detail: https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html

Random crash with core data, possible concurrency issue

You're not doing Core Data concurrency correctly. The right way is to use the perform and performAndWait methods on NSManagedObjectContext. Using a context in an operation queue like your code does is a recipe for concurrency related bugs.

Dispatch queues don't help you here. You can still use dispatch queues, but you must also use Core Data's concurrency methods to avoid problems.



Related Topics



Leave a reply



Submit