在win_mutex锁boost :: asio :: io_service崩溃
我一直有一个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对象。
希望我今天过得不好,并且调用未定义的行为。 否则,我想了解为什么会发生这种情况?提前致谢。
的问题是,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反应器。
真棒答案,如果可以的话,我会upvote,但没有足够的代表。欣赏细节;讨厌宏的另一个原因! – lukecfg
问题是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
感谢您的回复。但是,除非我错过了一些东西,否则我认为这个问题与io_service在这个特定情况下的生命周期无关。我添加了包装器'foo_base_io_service'作为跟踪io_service生命周期的机制。我编辑了上面的代码来在析构函数中添加一条print语句。析构函数永远不会被调用,表明该对象在崩溃时仍然存在。我还添加了一个外部io_service变量'test_io_service'来绕过包装类。 test_io_service对象产生相同的崩溃。 – lukecfg
我的直觉说它是[静态初始化Fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order)比(线程)数据竞争更快。 – sehe
@sehe同意。作为一个可能的原因,失败是有道理的。更改foo_base_io_service的链接和交换标头包含顺序都可以触发静态初始化排序中的更改。 – lukecfg
@lukecfg,到目前为止的任何解决方案? – Jithendra