libuv 定時器錯誤使用引發的慘案
阿新 • • 發佈:2018-03-29
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 定時器錯誤使用引發的慘案