2015-07-20 179 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

相关问题