Skip to content

Commit e288c5a

Browse files
committed
Bug: Yielding in a hook stops in the wrong instruction
Yielding in a hook must decrease the program counter, because it already counted an instruction that, in the end, was not executed. However, that decrement should be done only when about to restart the thread. Otherwise, inspecting the thread with the debug library shows it one instruction behind of where it really is.
1 parent 5853c37 commit e288c5a

File tree

3 files changed

+11
-6
lines changed

3 files changed

+11
-6
lines changed

ldebug.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -925,12 +925,12 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
925925
}
926926
pc++; /* reference is always next instruction */
927927
ci->u.l.savedpc = pc; /* save 'pc' */
928-
counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
928+
counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
929929
if (counthook)
930930
resethookcount(L); /* reset count */
931931
else if (!(mask & LUA_MASKLINE))
932932
return 1; /* no line hook and count != 0; nothing to be done now */
933-
if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
933+
if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
934934
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
935935
return 1; /* do not call hook again (VM yielded, so it did not move) */
936936
}
@@ -952,7 +952,6 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
952952
if (L->status == LUA_YIELD) { /* did hook yield? */
953953
if (counthook)
954954
L->hookcount = 1; /* undo decrement to zero */
955-
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
956955
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
957956
luaD_throw(L, LUA_YIELD);
958957
}

ldo.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,10 @@ static void resume (lua_State *L, void *ud) {
792792
lua_assert(L->status == LUA_YIELD);
793793
L->status = LUA_OK; /* mark that it is running (again) */
794794
if (isLua(ci)) { /* yielded inside a hook? */
795+
/* undo increment made by 'luaG_traceexec': instruction was not
796+
executed yet */
797+
lua_assert(ci->callstatus & CIST_HOOKYIELD);
798+
ci->u.l.savedpc--;
795799
L->top.p = firstArg; /* discard arguments */
796800
luaV_execute(L, ci); /* just continue running Lua code */
797801
}

testes/coroutine.lua

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,18 +610,20 @@ else
610610
-- (bug in 5.2/5.3)
611611
c = coroutine.create(function (a, ...)
612612
T.sethook("yield 0", "l") -- will yield on next two lines
613-
assert(a == 10)
613+
local b = a
614614
return ...
615615
end)
616616

617617
assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
618618
local n,v = debug.getlocal(c, 0, 1) -- check its local
619-
assert(n == "a" and v == 1)
619+
assert(n == "a" and v == 1 and debug.getlocal(c, 0, 2) ~= "b")
620620
assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
621621
local t = debug.getinfo(c, 0) -- test 'getinfo'
622-
assert(t.currentline == t.linedefined + 1)
622+
assert(t.currentline == t.linedefined + 2)
623623
assert(not debug.getinfo(c, 1)) -- no other level
624624
assert(coroutine.resume(c)) -- run next line
625+
local n,v = debug.getlocal(c, 0, 2) -- check next local
626+
assert(n == "b" and v == 10)
625627
v = {coroutine.resume(c)} -- finish coroutine
626628
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
627629
assert(not coroutine.resume(c))

0 commit comments

Comments
 (0)