1. 程式人生 > >libuv 定時器錯誤使用引發的慘案

libuv 定時器錯誤使用引發的慘案

cor def libuv form one std try lose next

今天我們正在開發的遊戲在測試過程中,服務器又掛了,用gdb加載core文件後看到最後的堆棧信息如下

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fa57d86da66 in uv_timer_init (loop=0x7fa57da7bc80 <default_loop_struct>, handle=0x2081e28) at src/unix/timer.c:55
#1  0x000000000043d326 in Room::Room(int, bool) ()
#2  0x00000000004437e7 in RoomManager::creatRoom(std::vector<Player, std::allocator<Player> >&) ()
#
3 0x0000000000491506 in AsyncCreateRoomTask::run() () #4 0x000000000040accf in timer_cb(uv_timer_s*) () #5 0x00007fa57d86df1e in uv__run_timers (loop=loop@entry=0x7fa57da7bc80 <default_loop_struct>) at src/unix/timer.c:165 #6 0x00007fa57d861f72 in uv_run (loop=0x7fa57da7bc80 <default_loop_struct>, mode=UV_RUN_DEFAULT) at src/unix/core.c:350
#7 0x0000000000409ca9 in main ()

查看libuv的源碼,是下面代碼引起的錯誤

uv__handle_init(loop, (uv_handle_t*) handle, UV_TIMER);

對應的宏定義是

#define uv__handle_init(loop_, h, type_)                                        do {                                                                            (h)->loop = (loop_);                                                          (h)
->type = (type_); (h)->flags = UV__HANDLE_REF; /* Ref the loop when active. */ QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); uv__handle_platform_init(h); } while (0)

檢查了loop和uv_timer_t均為有效指針,並且排除有多線程的競爭操作。

查看uv_timer_t的loop和type以及flags都正常賦值,於是基本鎖定錯誤源在QUEUE_INSERT_TAIL 這個插入隊列尾部的操作。 在尾部插入操作中,需要將loop的pre指向節點的next設置為uv_timer_t,但是讀取loop的pre指向的是一個未分配內存的地址,然後觸發了段錯誤。 下午幾個小時經過細致的代碼檢查,終於找到了錯誤原因:libuv一個timer的結束需要調用uv_close,而原來一直只調用了uv_timer_stop,並且把uv_timer_t給delete掉了。由於沒有調用uv_close,uv_timer_t還在loop的handle_queue中,在很小的機會下,系統之後又將原來這個uv_timer_t的空間分配給了其他對象,而這個對象剛好又修改到了原來handle_queue的next指針,讓它的變成了一個未分配的內存地址,然後在新定時器加入進行插入隊列的操作時候觸發了段錯誤。

libuv 定時器錯誤使用引發的慘案