Skip to content

Commit

Permalink
refactor handle gc, that maybe called before close_cb
Browse files Browse the repository at this point in the history
Lua `os.exit` prioritize `userdata` gc, cause `reference` failed.
  • Loading branch information
zhaozg committed Aug 14, 2023
1 parent 054b0f1 commit ff5e902
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 12 deletions.
19 changes: 15 additions & 4 deletions src/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,19 @@ static int luv_is_closing(lua_State* L) {
return 1;
}

static void luv_handle_free(uv_handle_t* handle);

static void luv_close_cb(uv_handle_t* handle) {
lua_State* L;
luv_handle_t* data = (luv_handle_t*)handle->data;
if (!data) return;
L = data->ctx->L;
luv_call_callback(L, data, LUV_CLOSED, 0);
luv_unref_handle(L, data);
if(data->ref > 0) {
luv_call_callback(L, data, LUV_CLOSED, 0);
luv_unref_handle(L, data);
} else {
luv_handle_free(handle);
}
}

static int luv_close(lua_State* L) {
Expand Down Expand Up @@ -127,19 +133,24 @@ static void luv_gc_cb(uv_handle_t* handle) {
static int luv_handle_gc(lua_State* L) {
uv_handle_t** udata = (uv_handle_t**)lua_touserdata(L, 1);
uv_handle_t* handle = *udata;
luv_handle_t* data = (luv_handle_t*)handle->data;

// Only cleanup if the handle hasn't been cleaned up yet.
if (handle) {
if (data->ref == LUA_NOREF) {
if (!uv_is_closing(handle)) {
// If the handle is not closed yet, close it first before freeing memory.
uv_close(handle, luv_gc_cb);
uv_close(handle, luv_handle_free);
}
else {
// Otherwise, free the memory right away.
luv_handle_free(handle);
}
// Mark as cleaned up by wiping the dangling pointer.
*udata = NULL;
} else {
// os.exit maybe cause gc before close_cb
// use LUA_REFNIL to tell close_cb to free memory.
data->ref = LUA_REFNIL;
}

return 0;
Expand Down
1 change: 1 addition & 0 deletions src/lhandle.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static void luv_call_callback(lua_State* L, luv_handle_t* data, luv_callback_id

static void luv_unref_handle(lua_State* L, luv_handle_t* data) {
luaL_unref(L, LUA_REGISTRYINDEX, data->ref);
data->ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, data->callbacks[0]);
luaL_unref(L, LUA_REGISTRYINDEX, data->callbacks[1]);
}
Expand Down
16 changes: 8 additions & 8 deletions tests/manual-test-exit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
local uv = require('luv')

local function setTimeout(callback, ms)
local timer = uv.new_timer()
timer:start(ms, 0, function()
timer:stop()
timer:close()
callback()
end)
return timer
local timer = uv.new_timer()
timer:start(ms, 0, function()
timer:stop()
timer:close()
callback()
end)
return timer
end

setTimeout(function()
os.exit(5, true)
os.exit(5, true)
end, 1000)

uv.run()

0 comments on commit ff5e902

Please sign in to comment.