Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions regress/145-assertion-on-dead.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/sh
_=[[
. "${0%%/*}/regress.sh"
exec runlua "$0" "$@"
]]
require"regress".export".*"

local co = coroutine.create(function()
coroutine.yield()
end)
coroutine.resume(co) -- kick off coroutine
coroutine.resume(co) -- resume a yield with no arguments
-- co is now dead

local cq = require"cqueues".new()
cq:attach(co)
check(cq:step())

say"OK"

23 changes: 17 additions & 6 deletions src/cqueues.c
Original file line number Diff line number Diff line change
Expand Up @@ -1982,10 +1982,22 @@ static cqs_status_t cqueue_resume(lua_State *L, struct cqueue *Q, struct callinf
}
} else {
nargs = lua_gettop(T->L);
if (status != LUA_YIELD) {
if (status == LUA_OK && lua_getstack(T->L, 0, &(lua_Debug){}) > 0) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This little bit was adapted from the coroutine.status implementation: http://www.lua.org/source/5.3/lcorolib.c.html#luaB_costatus

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

/* already running */
lua_pushliteral(L, "cannot resume non-suspended coroutine");
I->error.value = lua_gettop(L);
err_setthread(L, I, T);
goto defunct;
} else if ((status != LUA_OK && status != LUA_YIELD) || nargs == 0) {
/* dead coroutine */
lua_pushliteral(L, "cannot resume dead coroutine");
I->error.value = lua_gettop(L);
err_setthread(L, I, T);
goto defunct;
} else if (status == LUA_OK) {
/* initial */
nargs -= 1;
assert(nargs >= 0);
}
} /* else normal yield */
}

timer_del(Q, &T->timer);
Expand Down Expand Up @@ -2294,9 +2306,8 @@ static int cqueue_wrap(lua_State *L) {
luaL_checktype(L, 2, LUA_TFUNCTION);

newL = lua_newthread(L);
for (i = 2; i <= top; i++) {
lua_pushvalue(L, i);
}
lua_insert(L, 2);
luaL_checkstack(newL, top - 1, "too many arguments");
lua_xmove(L, newL, top - 1);

thread_add(L, Q, &I, -1);
Expand Down