aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-01-11 13:44:16 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-01-11 13:44:16 -0300
commite288c5a91883793d14ed9e9d93464f6ee0b08915 (patch)
treef563f9b4d218c9e35ff3e56eda068cfddb039651
parent5853c37a83ec66ccb45094f9aeac23dfdbcde671 (diff)
downloadlua-e288c5a91883793d14ed9e9d93464f6ee0b08915.tar.gz
lua-e288c5a91883793d14ed9e9d93464f6ee0b08915.tar.bz2
lua-e288c5a91883793d14ed9e9d93464f6ee0b08915.zip
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.
-rw-r--r--ldebug.c5
-rw-r--r--ldo.c4
-rw-r--r--testes/coroutine.lua8
3 files changed, 11 insertions, 6 deletions
diff --git a/ldebug.c b/ldebug.c
index b1f16ac9..d6f132ea 100644
--- a/ldebug.c
+++ b/ldebug.c
@@ -925,12 +925,12 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
925 } 925 }
926 pc++; /* reference is always next instruction */ 926 pc++; /* reference is always next instruction */
927 ci->u.l.savedpc = pc; /* save 'pc' */ 927 ci->u.l.savedpc = pc; /* save 'pc' */
928 counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); 928 counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
929 if (counthook) 929 if (counthook)
930 resethookcount(L); /* reset count */ 930 resethookcount(L); /* reset count */
931 else if (!(mask & LUA_MASKLINE)) 931 else if (!(mask & LUA_MASKLINE))
932 return 1; /* no line hook and count != 0; nothing to be done now */ 932 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? */
934 ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ 934 ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
935 return 1; /* do not call hook again (VM yielded, so it did not move) */ 935 return 1; /* do not call hook again (VM yielded, so it did not move) */
936 } 936 }
@@ -952,7 +952,6 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
952 if (L->status == LUA_YIELD) { /* did hook yield? */ 952 if (L->status == LUA_YIELD) { /* did hook yield? */
953 if (counthook) 953 if (counthook)
954 L->hookcount = 1; /* undo decrement to zero */ 954 L->hookcount = 1; /* undo decrement to zero */
955 ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
956 ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ 955 ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
957 luaD_throw(L, LUA_YIELD); 956 luaD_throw(L, LUA_YIELD);
958 } 957 }
diff --git a/ldo.c b/ldo.c
index bd8d965f..ea052950 100644
--- a/ldo.c
+++ b/ldo.c
@@ -792,6 +792,10 @@ static void resume (lua_State *L, void *ud) {
792 lua_assert(L->status == LUA_YIELD); 792 lua_assert(L->status == LUA_YIELD);
793 L->status = LUA_OK; /* mark that it is running (again) */ 793 L->status = LUA_OK; /* mark that it is running (again) */
794 if (isLua(ci)) { /* yielded inside a hook? */ 794 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--;
795 L->top.p = firstArg; /* discard arguments */ 799 L->top.p = firstArg; /* discard arguments */
796 luaV_execute(L, ci); /* just continue running Lua code */ 800 luaV_execute(L, ci); /* just continue running Lua code */
797 } 801 }
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index de7e46fb..e566c86e 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -610,18 +610,20 @@ else
610 -- (bug in 5.2/5.3) 610 -- (bug in 5.2/5.3)
611 c = coroutine.create(function (a, ...) 611 c = coroutine.create(function (a, ...)
612 T.sethook("yield 0", "l") -- will yield on next two lines 612 T.sethook("yield 0", "l") -- will yield on next two lines
613 assert(a == 10) 613 local b = a
614 return ... 614 return ...
615 end) 615 end)
616 616
617 assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine 617 assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
618 local n,v = debug.getlocal(c, 0, 1) -- check its local 618 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")
620 assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal' 620 assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
621 local t = debug.getinfo(c, 0) -- test 'getinfo' 621 local t = debug.getinfo(c, 0) -- test 'getinfo'
622 assert(t.currentline == t.linedefined + 1) 622 assert(t.currentline == t.linedefined + 2)
623 assert(not debug.getinfo(c, 1)) -- no other level 623 assert(not debug.getinfo(c, 1)) -- no other level
624 assert(coroutine.resume(c)) -- run next line 624 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)
625 v = {coroutine.resume(c)} -- finish coroutine 627 v = {coroutine.resume(c)} -- finish coroutine
626 assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) 628 assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
627 assert(not coroutine.resume(c)) 629 assert(not coroutine.resume(c))