2015-07-20 180 views
3

我一直有一個boost :: asio的問題,其中計時器和/或使用全局io_service實例創建的套接字在構建過程中崩潰。在發生崩潰的系統如下:在win_mutex鎖boost :: asio :: io_service崩潰

  • Windows 7的

  • 的Visual Studio 2013 Express的Windows桌面; v 12.0.31101.00 Update 4

  • Boost 1.57,動態鏈接,使用多線程編譯,例如, boost_thread-VC120-MT-GD-1_57.dll

我已經能夠複製問題在下面的簡化代碼:

//文件global_io_service.h

#ifndef INCLUDED_GLOBAL_IO_SERVICE_H 
#define INCLUDED_GLOBAL_IO_SERVICE_H 

#include <boost/asio/io_service.hpp> 

#include <iostream> 
#include <string> 

namespace foo{ 

extern boost::asio::io_service test_io_service; 

class foo_base_io_service{ 

public: 

    foo_base_io_service(const std::string& name) 
     : d_who_am_i(name) 
    { 
     std::cout << "constructing copy " << ++foo_base_io_service::num_instances << "my name is " << d_who_am_i << std::endl; 
    } 

    boost::asio::io_service& get_ref() 
    { 
     std::cout << "class requested copy of " << d_who_am_i << std::endl; 
     return d_ios; 
    } 

    ~foo_base_io_service() 
    { 
     std::cout << "Someone 86'd the base_io_service..." << std::endl; 
    } 

private: 

    // this class is not copyable 
    foo_base_io_service(const foo_base_io_service&); 
    foo_base_io_service& operator=(const foo_base_io_service&); 

    std::string d_who_am_i; 
    static int num_instances; 

    boost::asio::io_service d_ios; 
}; 

extern foo_base_io_service global_timer_io_service; 

} // namespace foo 

#endif 

//文件global_io_service.cpp

#include "global_io_service.h" 

namespace foo{ 
    boost::asio::io_service test_io_service; 

    foo_base_io_service global_timer_io_service("FOO_TIMER_SERVICE"); 

    // static initialization 
    int foo_base_io_service::num_instances = 0; 
} 

//文件的main.cpp

#include <WinSock2.h> 
#include "global_io_service.h" 
#include <boost/asio/deadline_timer.hpp> 

int main(int argc, char *argv[]) 
{ 
    // also causes crash 
    boost::asio::deadline_timer crash_timer2(foo::test_io_service);  

    // causes crash 
    boost::asio::deadline_timer crash_timer(foo::global_timer_io_service.get_ref()); 


    return 0 ; 
} 

這裏是飛機墜毀的回溯:

test_io_service.exe提高:: ASIO ::詳細:: win_mutex ::鎖()線51

test_io_service.exe提升! :ASIO ::詳細:: scoped_lock中:: scoped_lock的(提高:: ASIO ::詳細:: win_mutex &米)47號線

test_io_service.exe提高:: ASIO ::詳細:: win_iocp_io_service :: do_add_timer_queue(升壓:: asio :: detail :: timer_queue_base & queue)Line 477

test_io_service.exe!提高:: ASIO ::詳細:: win_iocp_io_service :: add_timer_queue>(升壓:: ASIO ::詳細:: timer_queue> &隊列)線79

test_io_service.exe!升壓:: ASIO ::詳細:: deadline_timer_service> :: deadline_timer_service>(升壓:: ASIO :: io_service對象& io_service對象)69號線

test_io_service.exe!提振:: ASIO :: deadline_timer_service> :: deadline_timer_service>(升壓:: ASIO :: io_service & io_service)78行

test_io_service.exe!boost :: asio: :detail :: service_registry :: create >>>(boost :: asio :: io_service & owner)81行

test_io_service.exe!boost :: asio :: detail :: service_registry :: do_use_service(const boost :: asio :: io_service對象::服務::關鍵&鍵,提高:: ASIO :: io_service對象::服務*(升壓:: ASIO :: io_service對象&)*工廠)線123

test_io_service.exe!提振:: ASIO :: detail :: service_registry :: use_service >>>()Line 49

test_io_service.exe!提高:: ASIO :: use_service>>(升壓:: ASIO :: io_service對象& IOS)線34

test_io_service.exe!提振:: ASIO :: basic_io_object>,0> :: basic_io_object>,0>(升壓:: ASIO :: io_service對象& io_service對象)線91

test_io_service.exe!提振:: ASIO :: basic_deadline_timer,提高:: ASIO :: deadline_timer_service>> :: basic_deadline_timer,提高:: ASIO :: deadline_timer_service>>(提高:: ASIO :: io_service對象& io_service對象)線151

test_io_service.exe!主(INT ARGC,CHAR *的argv)16號線C++

這是我學到的:

  • 的問題不會在Ubuntu 14.04,Ubuntu的14.10或Red Hat 6.5,提升1.54發生。
  • 此問題與Winsock2的加入順序有關。例如,與global_io_service.h交換包含的順序可消除崩潰。
  • 此問題與global_timer_io_service的外部鏈接有關。將global_timer_io_service的定義移動到main.cpp中可消除崩潰。
  • 我發現了在io_service內部關鍵部分發生類似崩潰的報告。這些問題主要與傳遞到定時器/套接字構造函數的io_service對象的生命週期有關。就我而言,我認爲我使用的io_service在輸入main之前已經構建完成。
  • 我的直覺說,有一個競爭條件(可能是WinSock2中的一些全局狀態設置?),阻止正確構建io_service對象。

希望我今天過得不好,並且調用未定義的行爲。 否則,我想了解爲什麼會發生這種情況?提前致謝。

+1

我的直覺說它是[靜態初始化Fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order)比(線程)數據競爭更快。 – sehe

+0

@sehe同意。作爲一個可能的原因,失敗是有道理的。更改foo_base_io_service的鏈接和交換標頭包含順序都可以觸發靜態初始化排序中的更改。 – lukecfg

+0

@lukecfg,到目前爲止的任何解決方案? – Jithendra

回答

4

的問題是,ASIO通過與否BOOST_ASIO_HAS_IOCP得到由boost/asio/detail/config.hpp定義選擇其在Windows io_service對象實施。如果定義,它將使用win_iocp_io_service。如果不是,它將使用task_io_service - 請參閱boost/asio/io_service.hpp。如果這個選擇在翻譯單元中是不同的,你最終將把io_service初始化爲一個,並將它用作另一個。它們以微妙的方式不同,例如什麼互斥體被初始化,所以這個問題可以表現爲由於使用未初始化的互斥體而導致的崩潰。

至於是什麼選擇BOOST_ASIO_HAS_IOCP,讓我們來看看config.hpp

#if !defined(BOOST_ASIO_HAS_IOCP) 
# if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) 
# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) 
# if !defined(UNDER_CE) 
# if !defined(BOOST_ASIO_DISABLE_IOCP) 
#  define BOOST_ASIO_HAS_IOCP 1 
# endif // !defined(BOOST_ASIO_DISABLE_IOCP) 
# endif // !defined(UNDER_CE) 
# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) 
# endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) 
#endif // !defined(BOOST_ASIO_HAS_IOCP) 

在這種情況下,爭議的宏_WIN32_WINNT,這似乎是越來越受WinSock2.h在你的項目中定義。因爲它在main.cpp定義,而不是在global_io_service.cpp定義,你初始化io_service對象使用task_io_service並調用它,如果它使用win_iocp_io_service

要解決的問題,或者適當地定義你的編譯器定義或全局頭_WIN32_WINNT文件,或者通過定義BOOST_ASIO_DISABLE_IOCP(全局地再次)關閉IOCP反應器。

+0

真棒答案,如果可以的話,我會upvote,但沒有足夠的代表。欣賞細節;討厭宏的另一個原因! – lukecfg

0

問題是yoor ioservice的一生。你把它從物體中取出來。

ioservice必須比所有服務壽命更長。

在此示例中 http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/tutorial/tuttimer2.html ioservice的壽命比最後期限計時器長。

編輯:這是從鮑里斯德國的在線圖書schäling http://dieboostcppbibliotheken.de/boost.asio-ioservices-und-objekte

+0

感謝您的回覆。但是,除非我錯過了一些東西,否則我認爲這個問題與io_service在這個特定情況下的生命週期無關。我添加了包裝器'foo_base_io_service'作爲跟蹤io_service生命週期的機制。我編輯了上面的代碼來在析構函數中添加一條print語句。析構函數永遠不會被調用,表明該對象在崩潰時仍然存在。我還添加了一個外部io_service變量'test_io_service'來繞過包裝類。 test_io_service對象產生相同的崩潰。 – lukecfg

相關問題