2016-03-17 15 views
5

Chúng tôi có ứng dụng OS X C++, sử dụng Qt 5.5 cung cấp giao diện máy chủ HTTP đơn giản. Đó là về cơ bản giống như ví dụ tạp chí Fortune máy chủ cung cấp bởi Qt (http://doc.qt.io/qt-5/qtnetwork-fortuneserver-example.html), nhưng những gì chúng ta đang thấy là thỉnh thoảng các ứng dụng được SEG đứt gãy khi tắt máy, với các vết đống sau (Chủ đề 6 bị rơi):Làm thế nào để ngăn chặn tình trạng cuộc đua tắt máy với các lớp mạng Qt

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 

Như bạn thấy, thread 0 được thực hiện - chúng ta đang ở bên ngoài chính. Tôi chắc rằng có một số mã dọn dẹp mà tôi không gọi được khi chúng tôi đang xử lý tài nguyên của mình, nhưng tôi không biết nó có thể là gì.

Nếu không đặt tất cả nguồn của chúng tôi lên đây, chuỗi cơ bản của các cuộc gọi được thực hiện là:

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())); 
} 

Sau đó, trong mã kiểm tra của chúng tôi, chúng tôi về cơ bản làm:

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); 
} 

Trả lời

0

Vấn đề dường như là do một số chủ đề mạng liên quan đến tạo ra bởi Qt vẫn chạy cho một chút sau khi cuộc gọi để QCoreApplication :: quit() được gọi, có lẽ vì quit() được gọi từ bên trong hàm gọi lại đã đăng ký với QTcpSocket. Giải pháp là đặt giấc ngủ 1 giây sau khi gọi để thoát:

void RestAPIServer_test::responseCallback(QNetworkReply *reply) 
{ 
    auto response = reply->readAll(); 
    _uri = response; 

    reply->close(); 

    QCoreApplication::exit(); 

    // This prevents a shutdown race condition where Qt service 
    // threads continue to run after the call to exit(). 
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
} 
0

Nó không phải là an toàn để khởi tạo QObject trên ngăn xếp với cha/mẹ. Nhìn vào dòng

QNetworkAccessManager networkManager(this); 

Đối tượng sẽ bị hủy hai lần, điều này là không thể, vì vậy bạn sẽ thấy sự cố. Thay đổi dòng đó như sau (tất nhiên, việc sử dụng các biến networkManager cũng phải được cập nhật):

QNetworkAccessManager* networkManager=new QNetworkAccessManager(this); 
+0

Cảm ơn bạn đã biết mẹo. Trên thực tế, một phiên bản trước của ví dụ này đã làm đúng những gì bạn nói, nhưng tôi đã thay đổi nó thành một biến ngăn xếp trong trường hợp này để xem vấn đề là do networkManager sống ngoài phạm vi của mã này. Trong cả hai trường hợp, segfault thường xuyên xảy ra. –

+0

Làm thế nào để bạn gọi 'quit()' trên 'QCoreApplication' để dừng vòng lặp' exec() '? Tôi sẽ đề nghị bạn chèn một khe proxy giữa tín hiệu của bạn và 'quit()' trong đó bạn có thể cố gắng phá hủy trình quản lý truy cập mạng máy chủ và máy chủ của bạn một cách rõ ràng. Sau đó, bạn có thể quảng bá tín hiệu đến vị trí 'quit()' trực tiếp hoặc sử dụng 'QTimer :: singleShot()' để đảm bảo rằng tất cả các luồng mạng được hoàn thành một cách chính xác. Xem kết quả bạn sẽ nhận được. –

+0

"Không an toàn để khởi tạo QObject trên ngăn xếp với cha mẹ." - điều đó chỉ đúng nếu cha mẹ có thể bị xóa trước đối tượng trên ngăn xếp. Nếu không, đối tượng sẽ bị xóa khỏi danh sách con của cha mẹ khi bị hủy, do đó không bị xóa hai lần –

Các vấn đề liên quan