J'ai un thread en arrière-plan effectuant un traitement et enregistrement dans Core Data. Dans la méthode de délégué d'application applicationShouldTerminate
, j'attends sur un sémaphore qui est libéré lorsque le thread en arrière-plan a terminé son travail. Ceci est fait pour éviter d'arrêter le thread en plein travail et laisser les choses dans un état incohérent.
Malheureusement, cela entraîne un deadlock. Voici comment fonctionne le travail en arrière-plan :
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_context setParentContext:_parentContext];
[_context performBlock:
^{
// ... tâche de longue durée ici ...
NSError * error;
[_context save:&error]; // deadlock ici si le thread principal attend sur le sémaphore
// ... libérer le sémaphore ici ...
}];
Si l'utilisateur quitte l'application alors que le thread en arrière-plan est toujours en cours, un deadlock se produit. Le problème semble être que [_context save:&error]
appelle dispatch_sync
(ou équivalent) sur le thread principal - mais le thread principal attend déjà que ce thread libère le sémaphore, et donc ne peut pas exécuter le bloc.
Comme une sauvegarde de contexte d'un contexte enfant semble bloquer le thread principal, comment peut-on réaliser ce schéma (le thread principal attendant que l'enfant termine et sauvegarde son contexte) ?
Thread principal :
#0 0x00007fff882e96c2 in semaphore_wait_trap ()
#1 0x00007fff876264c2 in _dispatch_semaphore_wait_slow ()
#2 0x00000001001157fb in +[IndxCore waitForBackgroundJobs] at /Users/mspong/dev/Indx/IndxCore/IndxCore/IndxCore.m:48
#3 0x00000001000040c6 in -[RHAppDelegate applicationShouldTerminate:] at /Users/mspong/dev/Indx/Indx/Indx/RHAppDelegate.m:324
#4 0x00007fff9071a48f in -[NSApplication _docController:shouldTerminate:] ()
#5 0x00007fff9071a39e in __91-[NSDocumentController(NSInternal) _closeAllDocumentsWithDelegate:shouldTerminateSelector:]_block_invoke_0 ()
#6 0x00007fff9071a23a in -[NSDocumentController(NSInternal) _closeAllDocumentsWithDelegate:shouldTerminateSelector:] ()
(snip)
#17 0x00007fff9048e656 in NSApplicationMain ()
#18 0x0000000100001e72 in main at /Users/mspong/dev/Indx/Indx/Indx/main.m:13
#19 0x00007fff8c4577e1 in start ()
Thread en arrière-plan :
#0 0x00007fff882e96c2 in semaphore_wait_trap ()
#1 0x00007fff87627c6e in _dispatch_thread_semaphore_wait ()
#2 0x00007fff87627ace in _dispatch_barrier_sync_f_slow ()
#3 0x00007fff8704a78c in _perform ()
#4 0x00007fff8704a5d2 in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] ()
#5 0x00007fff8702c278 in -[NSManagedObjectContext save:] ()
#6 0x000000010011640d in __22-[Indexer updateIndex]_block_invoke_0 at /Users/mspong/dev/Indx/IndxCore/IndxCore/Indexer/Indexer.m:70
#7 0x00007fff87079b4f in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync ()
#8 0x00007fff876230fa in _dispatch_client_callout ()
#9 0x00007fff876244c3 in _dispatch_queue_drain ()
#10 0x00007fff87624335 in _dispatch_queue_invoke ()
#11 0x00007fff87624207 in _dispatch_worker_thread2 ()
#12 0x00007fff88730ceb in _pthread_wqthread ()
#13 0x00007fff8871b1b1 in start_wqthread ()