Terminate an ongoing QProcess that is running inside a QThread? [duplicate] - linux

This question already has answers here:
Ensuring QProcess termination on termination of its parent QThread
(2 answers)
Closed 4 years ago.
how to terminate an ongoing QProcess that is running inside a QThread and gets deleted by another QThread? I even inserted a QMutex extCmdProcessLock, which should avoid the destruction of the DbManager before the extCmdProcess could finish or timeout.
I get a segmentation fault on "waitForStarted" if another thread calls delete on DbManager.
I cannot use signals (I think) because I use the external command inside a sequential data process.
Thank you very much for any help!
DbManager::extCmd(){
...
QMutexLocker locker(&extCmdProcessLock);
extCmdProcess = new QProcess(this);
QString argStr += " --p1=1"
+ " --p2=3";
extCmdProcess->start(cmd,argStr.split(QString(" ")));
bool startedSuccessfully = extCmdProcess->waitForStarted();
if (!startedSuccessfully) {
extCmdProcess->close();
extCmdProcess->kill();
extCmdProcess->waitForFinished();
delete extCmdProcess;
extCmdProcess = NULL;
return;
}
bool successfullyFinished = extCmdProcess->waitForFinished(-1);
if (!successfullyFinished) {
qDebug() << "finishing failed"; // Appendix C
extCmdProcess->close();
extCmdProcess->kill();
extCmdProcess->waitForFinished(-1);
delete extCmdProcess;
extCmdProcess = NULL;
return;
}
extCmdProcess->close();
delete extCmdProcess;
extCmdProcess = NULL;
}
DbManager::~DbManager(){
qDebug() << "DB DbManager destructor called.";
QMutexLocker locker(&extCmdProcessLock);
if (extCmdProcess!= NULL){
this->extCmdProcess->kill(); // added after Appendix A
this->extCmdProcess->waitForFinished();
}
}
Appendix A: I also get the error "QProcess: Destroyed while process is still running." and I read that this could mean that the "delete dbmanager" call from my other thread is executed while the waitForStarted() command has not completed. But I really wonder why the kill() command in my destructor has not fixed this.
Appendix B: According to comment, added waitForFinished(). Sadly, the QProcess termination still does not get shutdown properly, the segmentation fault happens in waitForStarted() or as below in start() itself.
#0 0x00007f25e03a492a in QEventDispatcherUNIX::registerSocketNotifier () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#1 0x00007f25e0392d0b in QSocketNotifier::QSocketNotifier () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#2 0x00007f25e0350bf8 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#3 0x00007f25e03513ef in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#4 0x00007f25e03115da in QProcess::start () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#5 0x0000000000428628 in DbManager::extCmd()
#6 0x000000000042ca06 in DbManager::storePos ()
#7 0x000000000044f51c in DeviceConnection::incomingData ()
#8 0x00000000004600fb in DeviceConnection::qt_metacall ()
#9 0x00007f25e0388782 in QObject::event () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#10 0x00007f25e0376e3f in QCoreApplicationPrivate::notify_helper () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#11 0x00007f25e0376e86 in QCoreApplication::notify () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#12 0x00007f25e0376ba4 in QCoreApplication::notifyInternal () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#13 0x00007f25e0377901 in QCoreApplicationPrivate::sendPostedEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#14 0x00007f25e03a4500 in QEventDispatcherUNIX::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#15 0x00007f25e0375e15 in QEventLoop::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#16 0x00007f25e0376066 in QEventLoop::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#17 0x00007f25e0277715 in QThread::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#18 0x00007f25e027a596 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#19 0x00007f25df9b43f7 in start_thread () from /lib/libpthread.so.0
#20 0x00007f25def89b4d in clone () from /lib/libc.so.6
#21 0x0000000000000000 in ?? ()
Appendix C: The debug output showed me, that the error message: QProcess: Destroyed while process is still running. always appears, when the finishing failed output appears. This means that my locks or/and kill attempts to protect the QProcess are failing.
Questions I wonder about:
a) If a create a QProcess object and start it, is my extCmdProcessLock unlocked? I already tried to use a normal lock() call instead of the QMutexLoader but no luck.
b) The docs say the main thread will be stopped if I use QProcess this way. Do they really mean the main thread or the thread in which QProcess is started? I assumed second.
c) is QProcess not usable in multithreading environment? If two threads create a QProcess object and run it, do they interfere? Maybe the object is somehow static?
Thanks for any help in filling the knowledge leaks. I really hope to get that puzzle solved.
Appendix D: After removing any delete and deleteLater() from any thread, my QProcess still gets smashed.
#0 0x00007fc94e9796b0 in QProcess::setProcessState () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#1 0x00007fc94e97998b in QProcess::waitForStarted () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#2 0x00007fc94e979a12 in QProcess::waitForFinished () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#3 0x0000000000425681 in DbManager::extCmd()
#4 0x0000000000426fb6 in DbManager::storePos ()
#5 0x000000000044d51c in DeviceConnection::incomingData ()
#6 0x000000000045fb7b in DeviceConnection::qt_metacall ()
#7 0x00007fc94e9f4782 in QObject::event () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#8 0x00007fc94e9e2e3f in QCoreApplicationPrivate::notify_helper () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#9 0x00007fc94e9e2e86 in QCoreApplication::notify () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#10 0x00007fc94e9e2ba4 in QCoreApplication::notifyInternal () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#11 0x00007fc94e9e3901 in QCoreApplicationPrivate::sendPostedEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#12 0x00007fc94ea10500 in QEventDispatcherUNIX::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#13 0x00007fc94e9e1e15 in QEventLoop::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#14 0x00007fc94e9e2066 in QEventLoop::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#15 0x00007fc94e8e3715 in QThread::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#16 0x00007fc94e8e6596 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#17 0x00007fc94e0203f7 in start_thread () from /lib/libpthread.so.0
#18 0x00007fc94d5f5b4d in clone () from /lib/libc.so.6
#19 0x0000000000000000 in ?? ()

It is really bad style to use a QThread to manage a running process. I'm seeing it again and again and it's some fundamental misunderstanding about how to write asynchronous applications properly. Processes are separate from your own application. QProcess provides a beautiful set of signals to notify you when it has successfully started, failed to start, and finished. Simply hook those signals to slots in an instance of a QObject-derived class of yours, and you'll be all set.
It's bad design if the number of threads in your application can exceed significantly the number of cores/hyperhtreads available on the platform, or if the number of threads is linked to some unrelated runtime factor like number of running subprocesses.
See my other other answer.
You can create QProcess on the heap, as a child of your monitoring QObject. You could connect QProcess's finished() signal to its own deleteLater() slot, so that it will automatically delete itself when it's done. The monitoring QObject should forcibly terminate any remaining running processes when it gets itself destroyed, say as a result of your application shutting down.
Further to the question was how to execute uncontrollably long running functions, say database queries for which there's no asynchronous API, with minimal impact, when interspersed with things for which there is good asynchronous API, such as QProcess.
A canonical way would be: do things synchronously where you must, asynchronously otherwise. You can stop the controlling object, and any running process, by invoking its deleteLater() slot -- either via a signal/slot connection, or using QMetaObject::invokeMethod() if you want to do it directly while safely crossing the thread boundary. This is the major benefit of using as few blocking calls as possible: you have some control over the processing and can stop it some of the time. With purely blocking implementation, there's no way to stop it short of using some flag variables and sprinkling your code with tests for it.
The deleteLater() will get processed any time the event loop can spin in the thread where a QObject lives. This means that it will get a chance between the database query calls -- any time when the process is running, in fact.
Untested code:
class Query : public QObject
{
Q_OBJECT
public:
Query(QObject * parent = 0) : QObject(parent) {
connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(error()));
connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(finished(int,QProcess::ExitStatus)));
}
~Query() { process.kill(); }
void start() {
QTimer::singleShot(0, this, SLOT(slot1()));
}
protected slots:
void slot1() {
// do a database query
process.start(....);
next = &Query::slot2;
}
protected:
// slot2 and slot3 don't have to be slots
void slot2() {
if (result == Error) {...}
else {...}
// another database query
process.start(...); // yet another process gets fired
next = &Query::slot3;
}
void slot3() {
if (result == Error) {...}
deleteLater();
}
protected slots:
void error() {
result = Error;
(this->*next)();
}
void finished(int code, QProcess::ExitStatus status) {
result = Finished;
exitCode = code;
exitStatus = status;
(this->*next)();
}
private:
QProcess process;
enum { Error, Finished } result;
int exitCode;
QProcess::ExitStatus exitStatus;
void (Query::* next)();
};
Personally, I'd check if the database that you're using has an asynchronous API. If it doesn't, but if the client library has available sources, then I'd do a minimal port to use Qt's networking stack to make it asynchronous. It would lower the overheads because you'd no more have one thread per database connection, and as you'd get closer to saturating the CPU, the overheads wouldn't rise: ordinarily, to saturate the CPU you'd need many, many threads, since they mostly idle. With asynchronous interface, the number of context switches would go down, since a thread would process one packet of data from the database, and could immediately process another packet from a different connection, without having to do a context switch: the execution stays within the event loop of that thread.

QProcess::waitForStarted just signals that your process has started. The mutex in extCmd() method gets unlocked then because you are not waiting for QProcess::waitForFinished in this method. You will exit this method while the child process is still running.
If you want to use a fire&forget type of execution I just you uses QProcess::startDetached

Related

QT application crashes when QWebFrame's evaluatingJavascript method called

I am using a QWebFrame to visualize some data and I use evalueateJavascript method to update data on Javascript. Here is my function to do this.
QWebFrame * webFrame;
void setValue(int idx, double value){
webPage->page()->mainFrame()->evaluateJavaScript(QString("setDataValue(%1,%2)").arg(QString::number(idx)).arg(QString::number(value)));
}
In QT application I can call this function via a button call back as many time without causing an error.
I want to call this setValue function from a separate thread to visualize incoming data. When I call setValue function from a separate thread, after few or first iteration application crash. I tried with both QThread and boost threads, but results are same.
void dummyTest(){
for(int i = 0; i < 1000; i++)
setValue(0,rand() % 150);
}
This dummyTest function is also working without problems when called via a button callback, but crash on running on a separate thread.
Here is the code for thread initialization
void startSerialProcessing() {
boost::thread_attributes attr;
attr.set_stack_size(1024);
std::cout << "dummy processor started. \n";
serialThread= new boost::thread(&MavLinkAL::dummyTest, this);
}
My observation is this crash only happens when setValue is called from a separate thread. Here is important lines from coredump file viewed from gdb.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f3f0d6f361f in WTF::StringImpl::~StringImpl() ()
from /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4
#1 0x00007f3f0d6583f8 in JSC::JSValue::toStringSlowCase(JSC::ExecState*) const
() from /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4
#2 0x00007f3f0d68a396 in ?? () from /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4
#3 0x00007f3f0d55a9f1 in ?? () from /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4
#4 0x00007f3f0d56313f in ?? () from /usr/lib/x86_64-linux-gnu/libQtWebKit.so.4
Any help to solve this problem is really appreciated. Thanks.
QWebFrame functions are neither reentrant nor thread safe. They can only be invoked from main GUI thread. Qt allows you to deliver a message to another thread quite easily. Make setValue function a slot, and connect it to a signal emitted from the processing thread.
EDIT: As Vladimir Bershov suggested in the comment below, one can also use QMetaObject::invokeMethod() with Qt::QueuedConnection to achieve the same result.

libexpect sigalrm nanosleep crash

I'm working with libexpect, but if the read times out (expected return code EXP_TIMEOUT) I instead get a crash as follows.
Program terminated with signal SIGABRT, Aborted.
#0 0x00007f1366275bb9 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007f1366275bb9 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007f1366278fc8 in __GI_abort () at abort.c:89
#2 0x00007f13662b2e14 in __libc_message (do_abort=do_abort#entry=2, fmt=fmt#entry=0x7f13663bf06b "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f136634a7dc in __GI___fortify_fail (msg=<optimized out>) at fortify_fail.c:37
#4 0x00007f136634a6ed in ____longjmp_chk () at ../sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S:100
#5 0x00007f136634a649 in __longjmp_chk (env=0x1, val=1) at ../setjmp/longjmp.c:38
#6 0x00007f1366ed2a95 in ?? () from /usr/lib/libexpect.so.5.45
#7 <signal handler called>
#8 0x00007f1367334b9d in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#9 0x000000000044cc13 in main (argc=3, argv=0x7fffca4013b8) at main_thread.c:6750
(gdb)
As you can see, I'm using nanosleep, which is supposed to not interact with signals like usleep and sleep (http://linux.die.net/man/2/nanosleep). As I understand it, libexpect uses SIGALRM to time out, but it's unclear to me how the two threads are interacting. If I had to guess, the expect call is raising a sigalrm, and it's interrupting the nanosleep call, but beyond that I don't know what's going on.
Thread 1:
while (stuff)
{
//dothings
struct timespec time;
time.tv_sec = 0.25;
time.tv_nsec = 250000000;
nanosleep(&time, NULL);
}
Thread 2:
switch(exp_expectl(fd, exp_glob, (char*)user_prompt, OK, exp_end))
{
case OK:
DG_LOG_DEBUG("Recieved user prompt");
break;
case EXP_TIMEOUT:
DG_LOG_DEBUG("Expect timed out");
goto error;
default:
DG_LOG_DEBUG("Expect failed for unknown reasons");
goto error;
}
I have done some reading about signals and sleep, but I've used sleep in multiple threads on many occasions and had no difficulties until now. What am I missing?
edit: misc version info
ubuntu 14.04 3.13.0-44-generic
/usr/lib/libexpect.so.5.45
code is in C
compiler is gcc (-lexpect -ltcl)
include <tcl8.6/expect.h>

CoreData deadlock with background thread

I am using the classic multiple MOCs sharing the same NSPersistentStoreCoordinator as I understand it performs better than the parent/child contexts according to this:
http://floriankugler.com/blog/2013/4/29/concurrent-core-data-stack-performance-shootout
I am being very careful about creating the contexts and using them on the thread/queue that creates them.
The deadlock is this:
Main thread is executing a fetch request and hung on a mutex in [NSManagedObjectContext executeFetchRequest:error:]
#0 0x3ad840fc in __psynch_mutexwait ()
#1 0x3accd128 in pthread_mutex_lock ()
#2 0x328eee90 in -[_PFLock lock] ()
#3 0x328ff350 in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] ()
#4 0x328fdf16 in -[NSManagedObjectContext executeFetchRequest:error:] ()
Background thread is hung on a lock I put on the persistent store coordinator prior to calling [coordinator managedObjectIDForURIRepresentation:objector].
#0 0x3ad840fc in __psynch_mutexwait ()
#1 0x3accd128 in pthread_mutex_lock ()
#2 0x328eee90 in -[_PFLock lock] ()
#3 0x00093180 in -[DataManager getObjectByUri:context:]
I understand that this is difficult to debug from a post but any ideas would be greatly appreciated. BTW...this happens rarely.

libspotify logout crashes when called from a nodeJS module

I am trying to write a module for nodeJS which wraps libspotify. The goal is to write a webapp that allows the remote control of a device playing music from spotify.
I have decided to go along the spshell example to ensure thread safety and write a "Spotify Service" in plain C that starts a seperate thread which calls all the API functions.
The nodeJS module then just calls a few provided functions to interact with spotify. The code for the service can be found here: http://pastebin.com/KB6uwSC8 The new thread gets started at the bottom.
Now, if i call this in a simple program like this (the fget is just to have a simple way for the login to complete). I used c++ to get as close to as node-gyp compiles the code.
#include <stdio.h>
extern "C" {
#include "objects/SpotifyService.h"
}
int main(int argc, char** argv) {
login();
char string[100];
fgets(string, 100, stdin);
fprintf(stdout, "Got: %s", string);
logout();
fgets(string, 100, stdin);
fprintf(stdout, "Got: %s", string);
return 0;
}
It works fine. I can't get this to crash.
If I use the same exact "Service" in nodeJS (meaning I just call login() and logout() and do nothing else), it crashes sometimes when logging out, like 7-8/10 times. I've tried lots of stuff, including:
Copying the compiler flags from node-gyp to my small example
fiddling with the thread attributes of the spotify thread
compiling on OSX and Debian
using libuv instead of plain pthreads
compiling my "service" to a shared library and call this from node
to no avail. It just crashes. It seems to crash less when called from within gdb, but that could be random.
A stack trace from gdb shows the following:
Thread 3 (Thread 0x7ffff65fd700 (LWP 21838)):
#0 0x00007ffff678f746 in ?? () from /usr/local/lib/libspotify.so.12
#1 0x00007ffff6702289 in ?? () from /usr/local/lib/libspotify.so.12
#2 0x00007ffff6702535 in ?? () from /usr/local/lib/libspotify.so.12
#3 0x00007ffff6703b5a in ?? () from /usr/local/lib/libspotify.so.12
#4 0x00007ffff6703c86 in ?? () from /usr/local/lib/libspotify.so.12
#5 0x00007ffff66c5c8b in ?? () from /usr/local/lib/libspotify.so.12
#6 0x00007ffff679a5b3 in sp_session_process_events () from /usr/local/lib/libspotify.so.12
#7 0x00007ffff6aa7839 in spotifyLoop (nervNicht=<value optimized out>) at ../src/SpotifyService.c:103
#8 0x00007ffff70118ca in start_thread () from /lib/libpthread.so.0
#9 0x00007ffff6d78b6d in clone () from /lib/libc.so.6
#10 0x0000000000000000 in ?? ()
(In OSX gdb showed that the function called in libspotify is called "process_title".)
Since nothing has helped so far i just don't have any idea if i can get this to work or if it is something in libspotify that's just incompatible with nodeJS. I don't understand how node-gyp links the .o files, maybe there something goes wrong?
I found two other projects on github that try to do this, but one of them puts the spotify main loop actually in Javascript and the other one uses node 0.1.100 and libspotify 0.0.4 and hasn't been updated in 2 years. I couldn't learn anything from both of them.
OK, i've played around some more. I just ignored the logout error and continued to implement other features.
I added a new sp_playlist_container creation in the logged_in callback and apparently that helped. After that, the node module does not crash anymore (or hasn't yet).
static sp_playlistcontainer_callbacks pc_callbacks = {
.container_loaded = &rootPlaylistContainerLoaded,
};
static void rootPlaylistContainerLoaded(sp_playlistcontainer* pc, void* userdata) {
int numPlaylists = sp_playlistcontainer_num_playlists(pc);
fprintf(stdout, "Root playlist synchronized, number of Playlists: %d\n", numPlaylists);
}
static void loggedIn(sp_session* session, sp_error error) {
if(SP_ERROR_OK != error) {
fprintf(stderr, "Error logging in: %s\n", sp_error_message(error));
} else {
fprintf(stdout, "Service is logged in!\n");
}
//This is absolutely necessary here, otherwise following callbacks can crash.
sp_playlistcontainer *pc = sp_session_playlistcontainer(spotifySession);
sp_playlistcontainer_add_callbacks(pc, &pc_callbacks, NULL);
}
But the sp_playlist_container creation must be in the logged_in callback, if i called it in another function (say, "getPlaylistNames") the program crashed, too.
I'll see if it continues to work and hope this answer can help others.

Core Data private queue deadlock

I have a background thread doing some processing, and saving to Core Data. In my app delegate's applicationShouldTerminate, I wait on a semaphore which is released when the background thread completes its work. This is to avoid killing the thread in the middle of its work and leaving things in an inconsistent state.
Unfortunately, this causes a deadlock. Here is how the background job operates:
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_context setParentContext:_parentContext];
[_context performBlock:
^{
// ... long-running task here ...
NSError * error;
[_context save:&error]; // deadlock here if main thread is waiting on semaphore
// ... release semaphore here ...
}];
If the user quits the app while the background thread is still working, it deadlocks. The problem seems to be that [_context save:&error] is calling dispatch_sync (or an equivalent) into the main thread - but the main thread is already waiting for this thread to release the semaphore, and so isn't able to run the block.
Since a child context save appears to block on the main thread, how can this pattern (main thread waiting for child to complete and save its context) be achieved?
Main thread:
#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 ()
Background thread:
#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 ()
In applicationShouldTerminate: you can return NSTerminateLater to delay the termination until the background context has finished the save operation.
When the saving is done, you call
[[NSApplication sharedApplication] replyToApplicationShouldTerminate:YES];
to quit the application.

Resources