libevent的loop怎麼停不下來?
(註一) |
啊~libevent是什麼?
有寫過網路程式的人應該對select()這個函式有印象。概念上可以把libevent想像成一個能把想要select的事件(ex. 某某socket能夠被讀或被寫),跟想要做的事情(ex. 在某某socket上交換資料),包成event-driven的跨平台函式庫。使用libevent,大部份的操作都是圍繞在struct event以及struct event_base這兩個結構上;其中一個是拿來存放socket、等待事件的flag、以及callback function的結構,另一個則是拿來存放event-loop的context的地方。
前陣子試著寫程式熟悉libevent的時候,發現在某些情況下在呼叫event_base_dispatch()之後,不管是用event_base_loopexit()或是event_base_loopbreak(),libevent的event-loop都沒辦法停下來。到底發生了什麼事呢?看一下event-loop的pseudo code(註二):
while (any events are registered with the loop) { /**< (1) */
if (loop should be terminated because of event_base_loopexit())
break; /**< (2) */
if (loop should be terminated because of event_base_loopbreak())
break; /**< (3) */
if (EVLOOP_NONBLOCK was set, or any events are already active)
If any registered events have triggered, mark them active.
else
/** blocked here!!! */
Wait until at least one event has triggered, and mark it active.
for (p = 0; p < n_priorities; p {
if (any event with priority of p is active) {
Run all active events with priority of p.
break; /* Do not run any events of a less important priority */
}
}
if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set)
break; /**< (4) */
}
看起來如果要讓event-loop能順利離開迴圈,只要以下任一條件滿足就可以:
- event_base已經沒有任何需要等待的event。
- event_base_loopexit()被呼叫。
- event_base_loopbreak()被呼叫。
- 在event-loop被啓動的時候有加EVLOOP_ONCE或EVLOOP_NONBLOCK的flag(迴圈只會執行一次就自動離開)。
似乎一切都很合乎邏輯呀!怎麼會卡在”Wait until…”那裡呢?
好吧,我的寫法是在event-loop裡面只等socket的事件,沒有指定任何timeout;如果迴圈一直等在”Wait until…”那一行,但是卻沒有任何socket的事件發生,那會發生什麼事呢?沒錯…就是無窮無盡地等待、等待、再等待…
那解法為何呢?一個是放個等待timeout的事件在event-loop裡,再利用event_base_loopexit()或event_base_loopbreak()就可以順利跳出(只不過worst case還是要等個timeout發生才能跳出…);另一個是手動把所有加進event-loop裡的事件通通移掉,移乾淨之後就沒有東西可以讓event-loop等待,event-loop最後就會自己結束了。
咦?怎麼要把event-loop停下來這麼麻煩呢?心裡暗自抱了這個疑問很久,很來想通了一件事:如果不用libevent,硬是用select()來包自己的while-loop的話,至少也會需要一個timeout,讓自己的select()有機會可以跳出while-loop;如果是用libevent,絕大多數的寫法,自然就會加入這個timeout,所以其實也是一樣的道理而已。
結論就是,我之前遇到的情況其實大部份的人都碰不到;如果真的有人寫法也是像我一樣怪的話,那就自己認命在event-loop裡多加個timeout等一等,或是手動把所有加進event-loop的event通通移掉吧。
(註一) 圖片來源:http://monkey.org/~provos/libevent/libevent-benchmark.jpg
(註二) Pseudo code改自:http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html
event_base_loopexit()函数内部就是添加了一个timeout函数,来设置base->event_gotterm
敢问楼主的意思是:自己写的event_loop()函数内部没有添加对timeout事件的处理吗?
因为现在我也碰到了一个类似的需求,在event_loop()的内部再启动一个event_loop(),但是又需要在内部的event_loop()调用的read/write事件中,适时得把内部这个loop给退掉,如果libevent提供的event_base_loopexit()不好用的话,确实就比较烦人了