文档章节

boost.asio的又一个bug.

Jackarain
 Jackarain
发布于 2011/07/13 00:25
字数 1206
阅读 8658
收藏 3

最近在设计一个多线程分块支持续传的http的异步客户端时, 测试部门经常发现http下载模
块退出时偶尔会卡住, 在win7系统上由为明显. 反复检查代码, 并未明显问题, 于是专门写
了一个反复退出的单元测试, 立即发现问题, 并定位在io_service的析构函数中, 奇怪的是,
我的投递io的所有socket都早已经关闭, run线程也已经退出, 按理说, 这时的io_service的
outstanding_work_应该为0才是, 可我一看它却是1, 于是始终在win_iocp_io_service.hpp的
shutdown_service里一直循环调用GetQueuedCompletionStatus, 从而导致无法正常退出...
很明显, 这是asio对outstanding_work_计数维护的有问题, 为了解决
问题, 于是我很快想到不使用iocp, 添加宏BOOST_ASIO_DISABLE_IOCP一切就正常了...
由于自己使用的是boost.1.45版本, 于是换了个boost.1.46.1再试试, 结果一样. 难道这么严
重的bug跨在了这两个非常重要的发行版本上而没人知道?
在官方的邮件列表中细节检查, 终于看到了某人的bug报告和我描述的情况差不多, 而且
在那个人报告了bug的第二天, asio作者就发布了补丁, 但这个补丁并未更新到boost.1.45
和boost.1.46中, 唉, 这两个版本可是大版本啊, 估计受害人不少...
不过幸运的是, 我在boost的主分枝中看到了修正的代码.
下面是这个补丁内容:

From 81a6a51c0cb66de6bc77e1fa5dcd46b2794995e4 Mon Sep 17 00:00:00 2001
From: Christopher Kohlhoff <chris@kohlhoff.com>
Date: Wed, 23 Mar 2011 15:03:56 +1100
Subject: [PATCH] On Windows, ensure the count of outstanding work is decremented for
 abandoned operations (i.e. operations that are being cleaned up within
 the io_service destructor).

---
 asio/include/asio/detail/impl/dev_poll_reactor.ipp |    2 ++
 asio/include/asio/detail/impl/epoll_reactor.ipp    |    2 ++
 asio/include/asio/detail/impl/kqueue_reactor.ipp   |    2 ++
 asio/include/asio/detail/impl/select_reactor.ipp   |    2 ++
 .../asio/detail/impl/signal_set_service.ipp        |    2 ++
 asio/include/asio/detail/impl/task_io_service.ipp  |    7 +++++++
 .../asio/detail/impl/win_iocp_io_service.ipp       |   11 +++++++++++
 asio/include/asio/detail/task_io_service.hpp       |    4 ++++
 asio/include/asio/detail/win_iocp_io_service.hpp   |    4 ++++
 9 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/asio/include/asio/detail/impl/dev_poll_reactor.ipp b/asio/include/asio/detail/impl/dev_poll_reactor.ipp
index 06d89ea..2a01993 100644
--- a/asio/include/asio/detail/impl/dev_poll_reactor.ipp
+++ b/asio/include/asio/detail/impl/dev_poll_reactor.ipp
@@ -63,6 +63,8 @@ void dev_poll_reactor::shutdown_service()
     op_queue_[i].get_all_operations(ops);
 
   timer_queues_.get_all_timers(ops);
+
+  io_service_.abandon_operations(ops);
 } 
 
 // Helper class to re-register all descriptors with /dev/poll.
diff --git a/asio/include/asio/detail/impl/epoll_reactor.ipp b/asio/include/asio/detail/impl/epoll_reactor.ipp
index 22f567a..d08dedb 100644
--- a/asio/include/asio/detail/impl/epoll_reactor.ipp
+++ b/asio/include/asio/detail/impl/epoll_reactor.ipp
@@ -84,6 +84,8 @@ void epoll_reactor::shutdown_service()
   }
 
   timer_queues_.get_all_timers(ops);
+
+  io_service_.abandon_operations(ops);
 }
 
 void epoll_reactor::fork_service(asio::io_service::fork_event fork_ev)
diff --git a/asio/include/asio/detail/impl/kqueue_reactor.ipp b/asio/include/asio/detail/impl/kqueue_reactor.ipp
index f0cdf73..45aff60 100644
--- a/asio/include/asio/detail/impl/kqueue_reactor.ipp
+++ b/asio/include/asio/detail/impl/kqueue_reactor.ipp
@@ -74,6 +74,8 @@ void kqueue_reactor::shutdown_service()
   }
 
   timer_queues_.get_all_timers(ops);
+
+  io_service_.abandon_operations(ops);
 }
 
 void kqueue_reactor::fork_service(asio::io_service::fork_event fork_ev)
diff --git a/asio/include/asio/detail/impl/select_reactor.ipp b/asio/include/asio/detail/impl/select_reactor.ipp
index f4e0314..00fd9fc 100644
--- a/asio/include/asio/detail/impl/select_reactor.ipp
+++ b/asio/include/asio/detail/impl/select_reactor.ipp
@@ -81,6 +81,8 @@ void select_reactor::shutdown_service()
     op_queue_[i].get_all_operations(ops);
 
   timer_queues_.get_all_timers(ops);
+
+  io_service_.abandon_operations(ops);
 }
 
 void select_reactor::fork_service(asio::io_service::fork_event fork_ev)
diff --git a/asio/include/asio/detail/impl/signal_set_service.ipp b/asio/include/asio/detail/impl/signal_set_service.ipp
index f0f0e78..4cde184 100644
--- a/asio/include/asio/detail/impl/signal_set_service.ipp
+++ b/asio/include/asio/detail/impl/signal_set_service.ipp
@@ -145,6 +145,8 @@ void signal_set_service::shutdown_service()
       reg = reg->next_in_table_;
     }
   }
+
+  io_service_.abandon_operations(ops);
 }
 
 void signal_set_service::fork_service(
diff --git a/asio/include/asio/detail/impl/task_io_service.ipp b/asio/include/asio/detail/impl/task_io_service.ipp
index cb585d5..0a2c6fa 100644
--- a/asio/include/asio/detail/impl/task_io_service.ipp
+++ b/asio/include/asio/detail/impl/task_io_service.ipp
@@ -230,6 +230,13 @@ void task_io_service::post_deferred_completions(
   }
 }
 
+void task_io_service::abandon_operations(
+    op_queue<task_io_service::operation>& ops)
+{
+  op_queue<task_io_service::operation> ops2;
+  ops2.push(ops);
+}
+
 std::size_t task_io_service::do_one(mutex::scoped_lock& lock,
     task_io_service::idle_thread_info* this_idle_thread)
 {
diff --git a/asio/include/asio/detail/impl/win_iocp_io_service.ipp b/asio/include/asio/detail/impl/win_iocp_io_service.ipp
index ca3125e..7aaa6b8 100644
--- a/asio/include/asio/detail/impl/win_iocp_io_service.ipp
+++ b/asio/include/asio/detail/impl/win_iocp_io_service.ipp
@@ -262,6 +262,17 @@ void win_iocp_io_service::post_deferred_completions(
   }
 }
 
+void win_iocp_io_service::abandon_operations(
+    op_queue<win_iocp_operation>& ops)
+{
+  while (win_iocp_operation* op = ops.front())
+  {
+    ops.pop();
+    ::InterlockedDecrement(&outstanding_work_);
+    op->destroy();
+  }
+}
+
 void win_iocp_io_service::on_pending(win_iocp_operation* op)
 {
   if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
diff --git a/asio/include/asio/detail/task_io_service.hpp b/asio/include/asio/detail/task_io_service.hpp
index 285d83e..654f83c 100644
--- a/asio/include/asio/detail/task_io_service.hpp
+++ b/asio/include/asio/detail/task_io_service.hpp
@@ -105,6 +105,10 @@ public:
   // that work_started() was previously called for each operation.
   ASIO_DECL void post_deferred_completions(op_queue<operation>& ops);
 
+  // Process unfinished operations as part of a shutdown_service operation.
+  // Assumes that work_started() was previously called for the operations.
+  ASIO_DECL void abandon_operations(op_queue<operation>& ops);
+
 private:
   // Structure containing information about an idle thread.
   struct idle_thread_info;
diff --git a/asio/include/asio/detail/win_iocp_io_service.hpp b/asio/include/asio/detail/win_iocp_io_service.hpp
index a562834..b5d7f0b 100644
--- a/asio/include/asio/detail/win_iocp_io_service.hpp
+++ b/asio/include/asio/detail/win_iocp_io_service.hpp
@@ -126,6 +126,10 @@ public:
   ASIO_DECL void post_deferred_completions(
       op_queue<win_iocp_operation>& ops);
 
+  // Enqueue unfinished operation as part of a shutdown_service operation.
+  // Assumes that work_started() was previously called for the operations.
+  ASIO_DECL void abandon_operations(op_queue<operation>& ops);
+
   // Called after starting an overlapped I/O operation that did not complete
   // immediately. The caller must have already called work_started() prior to
   // starting the operation.
-- 
1.7.0.1

注: 在boost.asio中, 使用这个补丁时, 需要将ASIO_DECL 改成 BOOST_ ASIO_DECL
注: 最新发布的boost1.47版本已经修复了这个bug, 建议尽量采用新版本的boost.

这是我第二次在使用asio的过程中, 发现的比较严重的bug了, 不过幸运的是, 每一次都能在官方的论坛 
或邮件列表中找到解决方案. 

结论, 再牛的人写的代码也会有bug, 个人非常崇拜asio的作者.

© 著作权归作者所有

Jackarain

Jackarain

粉丝 225
博文 17
码字总数 10627
作品 4
杭州
私信 提问
加载中

评论(2)

Jackarain
Jackarain 博主

引用来自“lin113”的评论

我用的boost1.47[1.47.0]的一个版本也有这个问题,哎,我测试了一天多,没办法,我也定义BOOST_ASIO_DISABLE_IOCP
支持boost,支持开源 [lin 2012/05/22]

boost1.47应该是没有这个问题的了呀? 不过没事, 尽量用最新版本吧
l
lin113
我用的boost1.47[1.47.0]的一个版本也有这个问题,哎,我测试了一天多,没办法,我也定义BOOST_ASIO_DISABLE_IOCP
支持boost,支持开源 [lin 2012/05/22]
Boost.Asio技术文档

Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE10.txt文件或从http://www.boost.org/LICENSE1_0.txt) Boost.Asio是用......

LUIS1983
2016/12/23
281
0
关于 Subversion 协议动态代理服务器

前言 在2015年下半年的时候,笔者的工作主要转向 GIT@OSC 分布式后端服务器的实现,GIT@OSC 分布式对于不同的接入有有不同的解决方案,HTTP 访问使用 nginx 模块实现动态代理,对于 ssh ,则...

Force武装卫队
2016/02/19
253
2
youngwolf/st_asio_wrapper

stasiowrapper Overview stasiowrapper is an asynchronous c/s framework based on Boost.Asio, besides all benefits brought by Boost and Boost.Asio, it also contains: Based on messa......

youngwolf
2015/04/29
0
0
Wine 1.1.11 版本发布

Wine 项目团队在今天发布了又一个开发版本 Wine 1.1.11。我们知道,上个版本 Wine 1.1.10 对 64 位支持进行了改善,而这个版本给我们带来了更多的 64 位支持──现在 Wine 支持使用 Mingw64 ...

红薯
2008/12/21
243
1
为 C++11 程序带来 restful 功能--Restbed

Restbed 框架为 C++11 构建的程序带来了 restful 功能,它基于 boost.asio 创建。 Restbed 可用于需要通过 HTTP 无缝和安全通信构建应用程序的全面和一致的编程模型,能够对一系列业务流程进...

匿名
2017/01/06
2.4K
2

没有更多内容

加载失败,请刷新页面

加载更多

使用原生css+js+html实现打印A4纸张的功能页面

有时候我们需要使用html+css实现打印A4纸张的功能页面,以下代码实现 <!DOCTYPE html><html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatibl......

b0cloud
18分钟前
2
0
读组件化之MGJRouter源码第二次的收获与思考

上一次我们写好了一个自定义的 路由类 ,然后我们来制作自己的 库 ,可以用来被 pod 引入 : 库的制作参考:https://www.jianshu.com/p/928d2ab053be 以下是我创建的: 利用上篇提到的 ,组件...

T型人才追梦者
19分钟前
1
0
spring cache、ehcache的使用及集成

项目中需要加缓存,故学习了 1、spring cache、ehcache的使用及集成 2、缓存的命中率等统计数据 一、spring cache 1、概述 Spring 3.1 引入了基于注解(annotation)的缓存(cache)技术 2、...

qkKing
21分钟前
3
0
Windows 10上源码编译Poco并编写httpserver和tcpserver | compile and install poco cpp library on windows

本文首发于个人博客https://kezunlin.me/post/9587bb47/,欢迎阅读! compile and install poco cpp library on windows Series guide to compile and install poco cpp library on windows g......

kezunlin
22分钟前
2
0
if-else-if-else与switch的区别

if-else-if-else: 适合分支较少 判断条件类型不单一 支持取 boolean 类型的所有运算 满足条件即停止对后续分支语句的执行 switch: 适合分支较多 判断条件类型单一,JDK 1.7 之前仅支持 in...

ConstXiong
22分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部