Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoreData error: API Misuse: Attempt to serialize store access on non-owning coordinator

Note:
This post does not apply since I actually use CoreData.
In this post the last answer suggests to fetch all items in the new background thread before adding new objects, but this is done in my code.
This post suggests to unfault an item before its context is saved, but this is also done in my code.

My app uses CoreData to store objects called shoppingItems. I wrote a class CoreDataManager that initialises CoreData and has essentially one function to overwrite the currently stored items, and one function to fetch all items. Both functions operate in the background, i.e. on a separate thread.

Here is my code (irrelevant parts are left out).

I setup core data on the main thread:

private lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: modelName)
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    })
    return container
}()  

This is the write function:

func overwriteShoppingItems(_ shoppingItems: Set<ShoppingItem>, completion: @escaping (Error?) -> Void) {
        let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        let viewContext = self.persistentContainer.viewContext
        backgroundContext.parent = viewContext
        backgroundContext.performAndWait {
            // Delete currently stored shopping items
            let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: CDEntityShoppingItem)
            do {
                let result = try backgroundContext.fetch(fetchRequest)
                let resultData = result as! [NSManagedObject]
                for object in resultData {
                    backgroundContext.delete(object)
                }

                if !shoppingItems.isEmpty {
                    // Save shopping items in managed context
                    let cdShoppingItemEntity = NSEntityDescription.entity(forEntityName: CDEntityShoppingItem, in: backgroundContext)!
                    for nextShoppingItem in shoppingItems {
                        let nextCdShoppingItem = CDShoppingItem(entity: cdShoppingItemEntity, insertInto: backgroundContext)
                        nextCdShoppingItem.name = nextShoppingItem.name
                    }
                }

                let saveError = self.saveManagedContext(managedContext: backgroundContext)
                completion(saveError)
            } catch let error as NSError {
                completion(error)
            }
        }
}

func saveManagedContext(managedContext: NSManagedObjectContext) -> Error? {
    if !managedContext.hasChanges { return nil }
    do {
        try managedContext.save()
        return nil
    } catch let error as NSError {
        return error
    }
}

And this is the fetch function:

func fetchShoppingItems(completion: @escaping (Set<ShoppingItem>?, Error?) -> Void) {
        let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        let viewContext = self.persistentContainer.viewContext
        backgroundContext.parent = viewContext
        backgroundContext.performAndWait {
            let fetchRequest: NSFetchRequest<CDShoppingItem> = CDShoppingItem.fetchRequest()
            do {
                let cdShoppingItems: [CDShoppingItem] = try backgroundContext.fetch(fetchRequest)
                guard !cdShoppingItems.isEmpty else {
                    completion([], nil)
                    return
                }
                for nextCdShoppingItem in cdShoppingItems {
                }
                    completion(shoppingItems, nil) 
            } catch let error as NSError {
                completion(nil, error)
            }
        }
}  

In normal operation, the code seems to work.

The problem:

I also wrote a unit test that tries to provoke multi-threading problems. This test uses a concurrent dispatch queue:

let concurrentReadWriteQueue = DispatchQueue(label: „xxx.test_coreDataMultithreading", attributes: .concurrent)  

A timer defines the test time.
In the scheme for tests, I have set the arguments -com.apple.CoreData.Logging.stderr 1 and -com.apple.CoreData.ConcurrencyDebug 1.
During the test time, overwriteShoppingItems and fetchShoppingItems are inserted repeatedly in the queue, and executed concurrently.
This unit test executes a few reads and writes, before it stops at the line

                    let itemName = nextCdShoppingItem.name!  

because nextCdShoppingItem.name is nil, which should never happen, because I never store nil.

Immediately before the crash, the following is logged:

CoreData: error:  API Misuse: Attempt to serialize store access on non-owning coordinator (PSC = 0x600000e6c980, store PSC = 0x0)

If I do only fetches, or only writes, the CoreData warning is not logged. Thus it seems definitely to be a multi-threading issue. However, CoreData.ConcurrencyDebug does not detect it.

It looks as if during a fetch operation on one thread, the other thread deletes the currently fetched item, so that its properties are read back as nil. But this should not happen because fetches and saves are done with backgroundContext.performAndWait, i.e. serially. And the stack trace shows that only a single thread accesses CoreData: Thread 3 Queue : NSManagedObjectContext 0x600003c8c000 (serial)

My questions:

  • What is my misuse of the CoreData API, and how to do it right?
  • Why is an item sometimes read back as nil?

EDIT:

Maybe this helps to identify the problem: When I comment out backgroundContext.delete(object) in overwriteShoppingItems, the error is no longer logged, and no item is fetched as nil.

like image 788
Reinhard Männer Avatar asked Sep 17 '25 15:09

Reinhard Männer


1 Answers

Okay so i got same error but only while "archiving" the project. Took me several hours to find the issue, since I was getting the error only on uploading, in the assets catalog, I had some duplicate images, by removing them the error is gone.

Anyone else getting this error AND your Coredata setup is fine, check your assets folder.

like image 142
ronix Avatar answered Sep 19 '25 05:09

ronix