Event_Handler在程序退出前应该自己关闭

来源:互联网 发布:淘宝网被罩 编辑:程序博客网 时间:2024/04/30 10:52

10         Event_Handler在程序退出前应该自己关闭

在程序退出的【注】,我们往往不会自己关闭Event_Handler,而寄希望Reactor 的清理。但是实际情况会复杂很多。使用的时候必须当心。

 

【注】是否要在退出的时候清理所有分配的内存?在普通的操作系统中,程序的退出会回收所有的分配内存。所以很多人会逃避在最后阶段的清理分配的内存。但是这实在不是一个良好的喜欢。一方面对于很多OS(比如嵌入系统)不会回收内存资源,一些内核资源(UNIX)也不会在进程退出后释放,编程就应该要养成清理的好习惯,更何况不进行释放在内存检查的软件一般会报错,如果不清理会干扰我们对于内存泄露的定位。

10.1           Reactorclose可能不会关闭Event_Handler

理论上讲,ACE_Reactor提供了一个close函数,所有的Event_Handler应该统一在这个函数进行关闭。

ACE_Reactor采用的是模式,封装了不同Reactor的实现。这些实现的close函数未存在一定的差异性。就我的阅读和尝试来看,Select_Reactorclose函数关闭了所有的IO句柄相关的Event_Handler,而Dev_Poll_Reactorclose实现就没有关闭。

Select_Reactorclose代码。

template <class ACE_SELECT_REACTOR_TOKEN> int

ACE_Select_Reactor_T<ACE_SELECT_REACTOR_TOKEN>::close (void)

{

  ……

  //handler_repclose函数会关闭所有的register的句柄的handler,调用他们的

  //handle_close函数

  this->handler_rep_.close ();

Dev_Poll_Reactorclose的调用了函数ACE_Dev_Poll_Reactor_Handler_Repository::close,而后有逐步调用了unbind_allremove_reference

//close会经过多级调用到ACE_Dev_Poll_Reactor_Handler_Repository:: unbind_all

//unbindunbind_all函数调用decr_refcnt == true

int

ACE_Dev_Poll_Reactor_Handler_Repository::unbind (ACE_HANDLE handle,

                                       bool decr_refcnt)

{……

  // remove_reference函数没有调用handle_close,而是减去了引用计数

    if (decr_refcnt)

    this->handlers_[handle].event_handler->remove_reference ();

 ……

 }

 

ACE_Event_Handler::Reference_Count

ACE_Event_Handler::remove_reference (void)

{

    //如果打开了引用计数,则使用应用计数方式管理方式。但是代码默认不采用应用计数模式

    //所以下面的代码都无法执行

    if (reference_counting_required)

    {

//减去引用计数

      Reference_Count result =

        --this->reference_count_;

      //如果已经没用引用个数了,删除自己。

      if (result == 0)

        delete this;

}

 

 

可以看到ACE_Event_Handler的代码默认不采用应用计数模式,(eference_counting_required默认为DISABLED)而Dev_Poll_Reactor却非要使用引用计数模式去清理Event_Handler

我对Dev_Poll_Reactor为什么要设计成这样表示不解。也对Dev_Poll_Reactor提交过BUG,但是Dev_Poll_Reactor的开发者不认为这样有什么不妥,本人E文羞涩,无法说服具体的开发人员,不过在提交BUG时,居然得到了Douglas反馈(他开始时认同我的看法),对于他们的执着和认真还是表示敬仰。

10.2           可能会导致重复释放引发Coredump

这个问题是在工作中调试一个BUG出现的。

在测试一个服务器的时候发现Coredump发生kill进程,让其退出在之后,会出现Coredump文件。Coredump显示出现问题的地方在。

#1  0x0805bc7b in ~ACE_Timer_Heap_T (this=0x82d3ec8) at /usr/local/ACE_wrappers/ace/Timer_Queue_T.cpp:442

#2  0x0805b86d in ~ACE_Singleton (this=0x82cca70) at egg_application.cpp:52

#3  0x08056785 in ACE_Singleton<EggSvrdAppliction, ACE_Null_Mutex>::cleanup (this=0x82dfb90)

由于希望改变ACE_Time_Queue的特性(数量),我替换Reactor的默认Time_Queue,所以必须自己销毁自己管理的TimeQueue。而在外部最后销毁的时候出现Coredump。由于和Time_Queue相关,我检查了所有的Timer相关的Event_handler,发现有一个Event_handler没有自己主动调用handler_close释放,这个Event_handler只有定时器,没有注册任何IO事件。修改代码为主动释放后,再次测试就发现Coredump的问题得到解决。

我检查了一下原有代码堆栈的调用顺序,找到了问题原因。

(1)ACE_Reactor::close,实际调用ACE_Select_Reactor::close

(2) Select_Reactor::close 尝试关闭所有的IO句柄相关的Event_handler,但由于Time_Queue是外部传入的参数,所以不清理Time_Queue

(3)Time_Queue清理,Time_Queue的析构函数被调用,Time_Queue的析构函数会释放所有的定时器相关的Event_handler。而他的释放还会调用hanlder_close。但是这是Reactor对象已经销毁了。所以造成了Coredump

注意由于Reactor的封装了Event_handler定时器,IO句柄,Notify机制等回调接口。所以Event_handler可能只关联到IO句柄,也可能只关联定时器,同时Reactor的模型决定了他的内部管理是复杂的。而在释放的过程中很可能会发生交错的问题,而,像上面问题的Event_handler就只关联的定时器,所以在Reactorclose的时候没有关闭。从而导致在后面的清理工作中产生时序问题。

最简单的方式还是自己在程序退出前清理释放所有的Event_handler.再调用Reactorclose

 

原创粉丝点击