-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix uv.shutdown cb being called when canceled #414
Conversation
This could happen after lua_close, and after absolutely all handles have been closed/freed, so there's no point in running the callback. There might be other legitimate situations where UV_ECANCELED should be passed along to the callback. If so, then there would need to be a more involved fix here to handle when the stream is closed via lua_close/__gc
Some debug printing to better explain what's happening:
Minimal reproduction: local uv = require('luv')
local stdin = uv.new_pipe(false)
local process, pid = uv.spawn(jit.os == "Windows" and "cmd.exe" or "cat", {
stdio = {stdin, nil, nil}
})
assert(uv.shutdown(stdin, function(...)
print("cb called", ...)
print(process)
end)) |
Worth noting that doing this from the Lua side also fixes this particular case: uv.shutdown(stdin, function(err)
if err == "ECANCELED" then
return
end
print(process)
end) So instead of doing this on the C side we could call the callback with ECANCELED (even during lua_close) and it would be up to the user to handle that case in their callback. |
What is prefer solution, lua side or c side? |
I think it would be better on the C side if we can be sure that |
https://github.com/libuv/libuv/search?q=UV_ECANCELED&unscoped_q=UV_ECANCELED shows some api will cause UV_ECANCELED, e.g. uv_cancel() |
I meant
Relevant Libuv test: |
Don't want to merge it yet, still not sure if handling this on the C side is correct and want to do some more testing. Take a look at #433, it's the more important thing to get fixed first. |
Going to focus on this today. |
This is not the right fix. The shutdown callback can be called with local uv = require('luv')
local isWindows = package.config:find("\\") and true or false
local stdin = uv.new_pipe(false)
handle, pid = uv.spawn(isWindows and "cmd.exe" or "cat", {
stdio = {stdin, nil, nil}
}, function(...)
print("onexit", ...)
end)
assert(uv.shutdown(stdin, function(...)
print("onshutdown", ...)
end))
uv.close(stdin, function(...)
print("onclose", ...)
end)
uv.run() (the call to |
The problem is that the shutdown callback is being called during loop_gc, so the Lua/Libuv state is being closed/free'd and the `handle` local var has already been closed/free'd so it's no longer a valid uv_stream_t (see luvit#414). This will fix PROCESS_CLEANUP_TEST but there is more to do to solve the general case of 'things happening during loop gc'
The problem is that the shutdown callback is being called during loop_gc, so the Lua/Libuv state is being closed/free'd and the `handle` local var has already been closed/free'd so it's no longer a valid uv_stream_t (see #414). This will fix PROCESS_CLEANUP_TEST but there is more to do to solve the general case of 'things happening during loop gc'
This could happen during lua_close, and after absolutely all handles have been closed, so there's no point in running the callback.
I'm not sure, but there might be other legitimate situations where UV_ECANCELED should be passed along to the callback. If so, then there would need to be a more involved fix here to handle when the stream is closed via lua_close/__gc
This is something I found when looking at our CI's
PROCESS_CLEANUP_TEST
jobs (see #193 and #194 for when/why they exist) and noticing that they were throwing a Lua error (and somehow not failing the CI?). I think this has been broken for a long time (likely ever sinceuv_handle_t
got a__gc
that closed itself).This is what happens when running
./tests/test-sigchld-after-lua_close.sh
This is not caused by not passing in a
uv_handle
userdata, but rather because theuv_handle
has been closed, andonshutdown
is being called duringlua_close
. Adding a print statement to theonshutdown
callback gives you this:i.e. the
__tostring
metamethod of the userdata is failing because the userdata is a closed (via __gc) uv_stream_t.