Wir haben eine OS X C++ - Anwendung mit Qt 5.5, die eine einfache HTTP-Server-Schnittstelle bietet. Es ist im Wesentlichen ähnlich das Fortune-Servers Beispiel von Qt zur Verfügung gestellt (http://doc.qt.io/qt-5/qtnetwork-fortuneserver-example.html), aber was wir sehen ist, dass gelegentlich die Anwendung seg beim Abschalten Verwerfungen mit der folgenden Stack-Trace (Gewinde 6 Krachen):So verhindern Sie Herunterfahren Race-Bedingungen mit Qt-Netzwerkklassen
Thread 0:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fff8deb4fca __open + 10
Thread 1:: Dispatch queue: com.apple.libdispatch-manager
0 libsystem_kernel.dylib 0x00007fff8deb6232 kevent64 + 10
1 libdispatch.dylib 0x00007fff90f0426e _dispatch_mgr_thread + 52
Thread 2:
0 libsystem_kernel.dylib 0x00007fff8deb594a __workq_kernreturn + 10
1 libsystem_pthread.dylib 0x00007fff887833dd start_wqthread + 13
Thread 3:
0 libsystem_kernel.dylib 0x00007fff8deb594a __workq_kernreturn + 10
1 libsystem_pthread.dylib 0x00007fff887833dd start_wqthread + 13
Thread 4:
0 libsystem_kernel.dylib 0x00007fff8deb594a __workq_kernreturn + 10
1 libsystem_pthread.dylib 0x00007fff887833dd start_wqthread + 13
Thread 5:: Dispatch queue: com.apple.NSXPCConnection.m-user.com.apple.airportd
0 libsystem_platform.dylib 0x00007fff8923378d _os_lock_handoff_lock + 23
1 libobjc.A.dylib 0x00007fff83258906 objc_object::sidetable_clearDeallocating() + 64
2 libobjc.A.dylib 0x00007fff8323e651 objc_destructInstance + 145
3 libobjc.A.dylib 0x00007fff8323e595 object_dispose + 22
4 com.apple.CoreFoundation 0x00007fff84eea448 -[__NSArrayM dealloc] + 376
5 libobjc.A.dylib 0x00007fff8325889c objc_object::sidetable_release(bool) + 236
6 com.apple.Foundation 0x00007fff85747909 -[_NSXPCInterfaceMethodInfo dealloc] + 63
7 libobjc.A.dylib 0x00007fff8325889c objc_object::sidetable_release(bool) + 236
8 com.apple.CoreFoundation 0x00007fff84ed5db0 CFRelease + 304
9 com.apple.CoreFoundation 0x00007fff84ee5b92 __CFBasicHashDrain + 498
10 com.apple.CoreFoundation 0x00007fff84ed5e8e CFRelease + 526
11 com.apple.Foundation 0x00007fff8578dd7a -[NSXPCInterface dealloc] + 28
12 libobjc.A.dylib 0x00007fff8325889c objc_object::sidetable_release(bool) + 236
13 com.apple.Foundation 0x00007fff8578df0c -[NSXPCConnection dealloc] + 281
14 libobjc.A.dylib 0x00007fff8325889c objc_object::sidetable_release(bool) + 236
15 libsystem_blocks.dylib 0x00007fff8d3166e5 _Block_release + 196
16 libdispatch.dylib 0x00007fff90effe73 _dispatch_client_callout + 8
17 libdispatch.dylib 0x00007fff90f035cd _dispatch_queue_drain + 1100
18 libdispatch.dylib 0x00007fff90f03030 _dispatch_queue_invoke + 202
19 libdispatch.dylib 0x00007fff90f02bef _dispatch_root_queue_drain + 463
20 libdispatch.dylib 0x00007fff90f02a1c _dispatch_worker_thread3 + 91
21 libsystem_pthread.dylib 0x00007fff88785a9d _pthread_wqthread + 729
22 libsystem_pthread.dylib 0x00007fff887833dd start_wqthread + 13
Thread 6 Crashed:: Qt bearer thread
0 org.qt-project.QtNetwork 0x0000000100a541cb QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() + 107
1 org.qt-project.QtNetwork 0x0000000100a5431e QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() + 14
2 org.qt-project.QtCore 0x0000000100d72427 QObject::event(QEvent*) + 823
3 org.qt-project.QtCore 0x0000000100d49588 QCoreApplication::notify(QObject*, QEvent*) + 104
4 org.qt-project.QtCore 0x0000000100d4a212 QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) + 1058
5 org.qt-project.QtCore 0x0000000100d997db QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 59
6 org.qt-project.QtCore 0x0000000100d46c1c QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 412
7 org.qt-project.QtCore 0x0000000100b9c07e QThread::exec() + 110
8 org.qt-project.QtCore 0x0000000100b9fc02 QThreadPrivate::start(void*) + 338
9 libsystem_pthread.dylib 0x00007fff8878605a _pthread_body + 131
10 libsystem_pthread.dylib 0x00007fff88785fd7 _pthread_start + 176
11 libsystem_pthread.dylib 0x00007fff887833ed thread_start + 13
Wie Sie sehen können, ist Thread 0 fertig - wir sind außerhalb von main. Ich bin mir sicher, dass es einen Cleanup-Code gibt, den ich nicht aufrufen kann, wenn wir unsere Ressourcen entsorgen, aber ich weiß nicht, was das sein kann.
Ohne all unsere Quelle hier oben zu setzen, ist die Grundkette Anrufe taten:
class RestServer : public QObject {
RestServer::RestServer() {
_tcpServer = new QTcpServer(this);
}
void RestServer::listen(quint16 port)
{
if (!_tcpServer->listen(QHostAddress::LocalHost, port)) {
LOG_ERROR("RestServer", "Failed to start server at: " << port);
throw std::exception();
}
_portNum = _tcpServer->serverPort();
LOG_INFO("RestServer", "Server is listening at: " << _portNum);
connect(_tcpServer, SIGNAL(newConnection()), this, SLOT(connectSocket()));
}
Dann in unserem Test-Code, das tun wir im Grunde:
void RestAPIServer_test::responseCallback(QNetworkReply *reply)
{
auto response = reply->readAll();
_uri = response;
reply->close();
QCoreApplication::exit();
}
TEST_F(RestAPIServer_test, urlWithPercents)
{
RestServer restServer();
restServer.listen(0);
quint16 port = restServer.serverPort();
// "widget/foo bar.txt"
QUrl serviceUrl(QString("http://localhost:%1/path/?path=widget%2Ffoo%20bar.txt").arg(port));
QNetworkAccessManager networkManager(this);
connect(&networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(responseCallback(QNetworkReply*)));
QNetworkRequest request(serviceUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
networkManager.get(request);
QCoreApplication::exec();
ASSERT_EQ(QString("path=widget/foo bar.txt"), _uri);
}
Danke für den Tipp. Tatsächlich hat eine frühere Version dieses Beispiels genau das gemacht, was Sie gesagt haben, aber ich habe es in diesem Fall in eine stack-Variable geändert, um zu sehen, ob das Problem darauf zurückzuführen ist, dass NetworkManager außerhalb des Bereichs dieses Codes lebt. In jedem Fall tritt der gelegentliche Segfault auf. –
Wie rufst du 'quit()' auf der 'QCoreApplication' auf, um die' exec() 'Schleife zu stoppen? Ich würde vorschlagen, dass Sie dort einen Proxy-Slot zwischen Ihrem Signal und 'quit()' einfügen, in dem Sie versuchen könnten, Ihren Server und Netzwerk-Zugriffsmanager explizit zu zerstören. Dann können Sie das Signal entweder direkt oder mit 'QTimer :: singleShot()' auf 'quit()' slot befördern, um sicherzustellen, dass alle Netzwerk-Threads korrekt beendet sind. Sehen Sie, welches Ergebnis Sie erhalten. –
"Es ist nicht sicher, ein QObject im Stapel mit einem Elternteil zu instanziieren." - Das ist nur dann der Fall, wenn das Elternobjekt möglicherweise vor dem Objekt im Stapel gelöscht werden kann. Andernfalls wird das Objekt aus der Kinderliste des Elternobjekts entfernt keine doppelte Löschung –