Event_Handler在程序退出前應(yīng)該自己關(guān)閉

字號(hào):

在程序退出的【注】,我們往往不會(huì)自己關(guān)閉Event_Handler,而寄希望Reactor 的清理。但是實(shí)際情況會(huì)復(fù)雜很多。使用的時(shí)候必須當(dāng)心。
    【注】是否要在退出的時(shí)候清理所有分配的內(nèi)存?在普通的操作系統(tǒng)中,程序的退出會(huì)回收所有的分配內(nèi)存。所以很多人會(huì)逃避在最后階段的清理分配的內(nèi)存。但是這實(shí)在不是一個(gè)良好的喜歡。一方面對(duì)于很多OS(比如嵌入系統(tǒng))不會(huì)回收內(nèi)存資源,一些內(nèi)核資源(UNIX)也不會(huì)在進(jìn)程退出后釋放,編程就應(yīng)該要養(yǎng)成清理的好習(xí)慣,更何況不進(jìn)行釋放在內(nèi)存檢查的軟件一般會(huì)報(bào)錯(cuò),如果不清理會(huì)干擾我們對(duì)于內(nèi)存泄露的定位。
    1 Reactor的close可能不會(huì)關(guān)閉Event_Handler
    理論上講,ACE_Reactor提供了一個(gè)close函數(shù),所有的Event_Handler應(yīng)該統(tǒng)一在這個(gè)函數(shù)進(jìn)行關(guān)閉。
    ACE_Reactor采用的是模式,封裝了不同Reactor的實(shí)現(xiàn)。這些實(shí)現(xiàn)的close函數(shù)未存在一定的差異性。就我的閱讀和嘗試來看,Select_Reactor在close函數(shù)關(guān)閉了所有的IO句柄相關(guān)的Event_Handler,而Dev_Poll_Reactor的close實(shí)現(xiàn)就沒有關(guān)閉。
    Select_Reactor的close代碼。
    template int
    ACE_Select_Reactor_T::close (void)
    {
    ……
    //在handler_rep的close函數(shù)會(huì)關(guān)閉所有的register的句柄的handler,調(diào)用他們的
    //handle_close函數(shù)
    this->handler_rep_.close ();
    Dev_Poll_Reactor的close的調(diào)用了函數(shù)ACE_Dev_Poll_Reactor_Handler_Repository::close,而后有逐步調(diào)用了unbind_all,remove_reference。
    //close會(huì)經(jīng)過多級(jí)調(diào)用到ACE_Dev_Poll_Reactor_Handler_Repository:: unbind_all
    //unbind被unbind_all函數(shù)調(diào)用decr_refcnt == true
    int
    ACE_Dev_Poll_Reactor_Handler_Repository::unbind (ACE_HANDLE handle,
    bool decr_refcnt)
    {……
    // remove_reference函數(shù)沒有調(diào)用handle_close,而是減去了引用計(jì)數(shù)
    if (decr_refcnt)
    this->handlers_[handle].event_handler->remove_reference ();
    ……
    }
    ACE_Event_Handler::Reference_Count
    ACE_Event_Handler::remove_reference (void)
    {
    //如果打開了引用計(jì)數(shù),則使用應(yīng)用計(jì)數(shù)方式管理方式。但是代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式
    //所以下面的代碼都無法執(zhí)行
    if (reference_counting_required)
    {
    //減去引用計(jì)數(shù)
    Reference_Count result =
    --this->reference_count_;
    //如果已經(jīng)沒用引用個(gè)數(shù)了,刪除自己。
    if (result == 0)
    delete this;
    }
    可以看到ACE_Event_Handler的代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式,(eference_counting_required默認(rèn)為DISABLED)而Dev_Poll_Reactor卻非要使用引用計(jì)數(shù)模式去清理Event_Handler。
    我對(duì)Dev_Poll_Reactor為什么要設(shè)計(jì)成這樣表示不解。也對(duì)Dev_Poll_Reactor提交過BUG,但是Dev_Poll_Reactor的開發(fā)者不認(rèn)為這樣有什么不妥,本人E文羞澀,無法說服具體的開發(fā)人員,不過在提交BUG時(shí),居然得到了Douglas反饋(他開始時(shí)認(rèn)同我的看法),對(duì)于他們的執(zhí)著和認(rèn)真還是表示敬仰。
    2 可能會(huì)導(dǎo)致重復(fù)釋放引發(fā)Coredump
    這個(gè)問題是在工作中調(diào)試一個(gè)BUG出現(xiàn)的。
    在測(cè)試一個(gè)服務(wù)器的時(shí)候發(fā)現(xiàn)Coredump發(fā)生kill進(jìn)程,讓其退出在之后,會(huì)出現(xiàn)Coredump文件。Coredump顯示出現(xiàn)問題的地方在。
    #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::cleanup (this=0x82dfb90)
    由于希望改變ACE_Time_Queue的特性(數(shù)量),我替換Reactor的默認(rèn)Time_Queue,所以必須自己銷毀自己管理的TimeQueue。而在外部最后銷毀的時(shí)候出現(xiàn)Coredump。由于和Time_Queue相關(guān),我檢查了所有的Timer相關(guān)的Event_handler,發(fā)現(xiàn)有一個(gè)Event_handler沒有自己主動(dòng)調(diào)用handler_close釋放,這個(gè)Event_handler只有定時(shí)器,沒有注冊(cè)任何IO事件。修改代碼為主動(dòng)釋放后,再次測(cè)試就發(fā)現(xiàn)Coredump的問題得到解決。
    檢查了一下原有代碼堆棧的調(diào)用順序,找到了問題原因。
    (1)ACE_Reactor::close,實(shí)際調(diào)用ACE_Select_Reactor::close
    (2) Select_Reactor::close 嘗試關(guān)閉所有的IO句柄相關(guān)的Event_handler,但由于Time_Queue是外部傳入的參數(shù),所以不清理Time_Queue。
    (3)Time_Queue清理,Time_Queue的析構(gòu)函數(shù)被調(diào)用,Time_Queue的析構(gòu)函數(shù)會(huì)釋放所有的定時(shí)器相關(guān)的Event_handler。而他的釋放還會(huì)調(diào)用hanlder_close。但是這是Reactor對(duì)象已經(jīng)銷毀了。所以造成了Coredump。
    注意由于Reactor的封裝了Event_handler定時(shí)器,IO句柄,Notify機(jī)制等回調(diào)接口。所以Event_handler可能只關(guān)聯(lián)到IO句柄,也可能只關(guān)聯(lián)定時(shí)器,同時(shí)Reactor的模型決定了他的內(nèi)部管理是復(fù)雜的。而在釋放的過程中很可能會(huì)發(fā)生交錯(cuò)的問題,而,像上面問題的Event_handler就只關(guān)聯(lián)的定時(shí)器,所以在Reactor的close的時(shí)候沒有關(guān)閉。從而導(dǎo)致在后面的清理工作中產(chǎn)生時(shí)序問題。
    最簡(jiǎn)單的方式還是自己在程序退出前清理釋放所有的Event_handler.再調(diào)用Reactor的close。