assertion hit after attaching dead coroutine#145
assertion hit after attaching dead coroutine#145daurnimator wants to merge 3 commits intowahern:masterfrom
Conversation
- Instead of re-pushing all arguments, just use lua_insert to get thread below args - Need to check the new stack to ensure we have enough slots
src/cqueues.c
Outdated
| } else { | ||
| nargs = lua_gettop(T->L); | ||
| if (status != LUA_YIELD) { | ||
| if (status == LUA_OK && lua_getstack(T->L, 0, &(lua_Debug){}) > 0) { |
There was a problem hiding this comment.
This little bit was adapted from the coroutine.status implementation: http://www.lua.org/source/5.3/lcorolib.c.html#luaB_costatus
There was a problem hiding this comment.
I think the test should be status == LUA_OK && lua_getstack(...) <= 0, or even better status == LUA_OK && lua_getstack(...) != 1 IIUC, nargs is decremented because in the initial state we want to exclude the function that will be called from the number of arguments provided to lua_resume.
Alternatively, just remove the assertion and don't decrement nargs if it's 0. If we've been given a dead coroutine or a running coroutine then lua_resume() will return a descriptive error, right?
There was a problem hiding this comment.
I think the test should be status == LUA_OK && lua_getstack(...) <= 0, or even better status == LUA_OK && lua_getstack(...) != 1
Why? does >0 vs <= 0 matter?
==> What I have is the same as what the lua coroutine library has.
This branch is what to do if the coroutine is already running; e.g.:
local cq = cqueues.new()
local co = coroutine.create(function()
print(cq:step())
end)
cq:attach(co)
coroutine.resume(co)If we've been given a dead coroutine or a running coroutine then lua_resume() will return a descriptive error, right?
A running coroutine, yes.
However, a dead coroutine is not handled well. Note that lua itself checks for this: http://www.lua.org/source/5.3/lcorolib.c.html#auxresume
There was a problem hiding this comment.
Why? does >0 vs <= 0 matter?
==> What I have is the same as what the lua coroutine library has.
The original code was assuming that if status != LUA_YIELD then it must be a newly created thread. That's why it was decrementing nargs--to exclude the function at the bottom of the stack (on the first invocation, lua_resume is like lua_pcall). lua_getstack returns 1 if the thread is running. We want to know whether the thread is in the initial state. If you look at the code in lcorolib.c, the non-yielding, suspended condition is only reached if status == LUA_OK && lua_getstack() <= 0. But using a condition of != 1 better matches the official documentation for lua_getstack.
As requested in wahern#145 (comment)
|
Changed. |
|
The issue is triggering the assert. The simplest solution is to not decrement nargs below 0. For everything else, lua_resume will report the error for us. Any reason why we're literally copying and reimplementing the code from ldo.c:resume? lua_resume will push the exact same error message onto the stack, and AFAICT we'll create the exact same error context (see the default: case for the switch on the status returned from lua_resume) afterward. If it's intended for improved diagnostic messages, shouldn't we put the check into cqueue_wrap so that it returns an error immediately? That way one would know what code was attaching the bogus coroutine. |
It just felt really "icky" doing that....
That wouldn't catch all cases: someone could |
|
Merged a different approach which removed the assertion. I was uncomfortable duplicating so much internal logic from the Lua VM. But leaving open because it's still unresolved whether it's worthwhile to specifically detect and report a dead coroutine (i.e. one that has successfully terminated) for the diagnostic/debugging benefit. Note 1: Cases other than a dead coroutine should be reported by lua_resume with the same messages as this pull request. |
Uh oh!
There was an error while loading. Please reload this page.