(註一) |
啊~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(迴圈只會執行一次就自動離開)。
好吧,我的寫法是在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