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能順利離開迴圈,只要以下任一條件滿足就可以:

  1. event_base已經沒有任何需要等待的event。
  2. event_base_loopexit()被呼叫。
  3. event_base_loopbreak()被呼叫。
  4. 在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



Comments
  1. lxyscls
    回覆

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *