Skip to content

Commit 7427958

Browse files
committed
Fix LUA_ERRKILL propagation with coroutine.resume()
1 parent d046770 commit 7427958

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

VM/src/lcorolib.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
7272
{
7373
return CO_STATUS_BREAK;
7474
}
75+
else if (status == LUA_ERRKILL)
76+
{
77+
// ServerLua: Propagate uncatchable termination errors to parent thread
78+
lua_xmove(co, L, 1); // move error message
79+
lua_killerror(L, lua_tostring(L, -1)); // kill parent thread
80+
// Never returns
81+
}
7582
else
7683
{
7784
lua_xmove(co, L, 1); // move error message
@@ -101,6 +108,15 @@ static int auxresumecont(lua_State* L, lua_State* co)
101108
else
102109
{
103110
lua_rawcheckstack(L, 2);
111+
112+
// ServerLua: Propagate uncatchable termination errors to parent thread
113+
if (co->status == LUA_ERRKILL)
114+
{
115+
lua_xmove(co, L, 1); // move error message
116+
lua_killerror(L, lua_tostring(L, -1)); // kill parent thread
117+
// Never returns
118+
}
119+
104120
lua_xmove(co, L, 1); // move error message
105121
return CO_STATUS_ERROR;
106122
}

VM/src/ldo.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,7 @@ static int resume_finish(lua_State* L, int status)
670670

671671
if (status != LUA_OK)
672672
{
673-
// ServerLua: Set ERRRUN on thread for ERRKILL (both are terminal errors)
674-
L->status = cast_byte(status == LUA_ERRKILL ? LUA_ERRRUN : status);
673+
L->status = cast_byte(status);
675674

676675
// ServerLua: Close upvalues for uncatchable termination errors
677676
if (status == LUA_ERRKILL)

tests/Conformance.test.cpp

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,7 +3297,7 @@ TEST_CASE("KillError")
32973297

32983298
interruptCount = 0;
32993299
int status = lua_resume(T, nullptr, 0);
3300-
CHECK(status == LUA_ERRRUN);
3300+
CHECK(status == LUA_ERRKILL);
33013301
CHECK(lua_isstring(T, -1));
33023302
CHECK(std::string(lua_tostring(T, -1)).find("Script terminated") != std::string::npos);
33033303

@@ -3324,7 +3324,7 @@ TEST_CASE("KillError")
33243324

33253325
interruptCount = 0;
33263326
int status = lua_resume(T, nullptr, 0);
3327-
CHECK(status == LUA_ERRRUN);
3327+
CHECK(status == LUA_ERRKILL);
33283328
CHECK(lua_isstring(T, -1));
33293329
CHECK(std::string(lua_tostring(T, -1)).find("Script terminated") != std::string::npos);
33303330

@@ -3357,6 +3357,87 @@ TEST_CASE("KillError")
33573357

33583358
lua_pop(L, 1);
33593359
}
3360+
3361+
// lua_killerror propagates through coroutine.resume()
3362+
{
3363+
static int interruptCount;
3364+
3365+
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
3366+
{
3367+
if (gc >= 0)
3368+
return;
3369+
if (++interruptCount >= 10)
3370+
{
3371+
interruptCount = 0;
3372+
lua_killerror(L, "Script terminated in child");
3373+
}
3374+
};
3375+
3376+
lua_State* T = lua_newthread(L);
3377+
lua_getglobal(T, "testcoroutine");
3378+
3379+
interruptCount = 0;
3380+
int status = lua_resume(T, nullptr, 0);
3381+
CHECK(status == LUA_ERRKILL);
3382+
CHECK(lua_isstring(T, -1));
3383+
CHECK(std::string(lua_tostring(T, -1)).find("Script terminated in child") != std::string::npos);
3384+
3385+
lua_pop(L, 1);
3386+
}
3387+
3388+
// lua_killerror propagates through nested coroutines
3389+
{
3390+
static int interruptCount;
3391+
3392+
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
3393+
{
3394+
if (gc >= 0)
3395+
return;
3396+
if (++interruptCount >= 10)
3397+
{
3398+
interruptCount = 0;
3399+
lua_killerror(L, "Script terminated in nested child");
3400+
}
3401+
};
3402+
3403+
lua_State* T = lua_newthread(L);
3404+
lua_getglobal(T, "testnestedcoroutine");
3405+
3406+
interruptCount = 0;
3407+
int status = lua_resume(T, nullptr, 0);
3408+
CHECK(status == LUA_ERRKILL);
3409+
CHECK(lua_isstring(T, -1));
3410+
CHECK(std::string(lua_tostring(T, -1)).find("Script terminated in nested child") != std::string::npos);
3411+
3412+
lua_pop(L, 1);
3413+
}
3414+
3415+
// lua_killerror propagates through coroutine.wrap()
3416+
{
3417+
static int interruptCount;
3418+
3419+
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
3420+
{
3421+
if (gc >= 0)
3422+
return;
3423+
if (++interruptCount >= 10)
3424+
{
3425+
interruptCount = 0;
3426+
lua_killerror(L, "Script terminated in wrapped coroutine");
3427+
}
3428+
};
3429+
3430+
lua_State* T = lua_newthread(L);
3431+
lua_getglobal(T, "testwrap");
3432+
3433+
interruptCount = 0;
3434+
int status = lua_resume(T, nullptr, 0);
3435+
CHECK(status == LUA_ERRKILL);
3436+
CHECK(lua_isstring(T, -1));
3437+
CHECK(std::string(lua_tostring(T, -1)).find("Script terminated in wrapped coroutine") != std::string::npos);
3438+
3439+
lua_pop(L, 1);
3440+
}
33603441
}
33613442

33623443
TEST_CASE("UserdataApi")

tests/conformance/killerror.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,30 @@ function testnested()
3030
error("should not reach here")
3131
end
3232

33+
-- Test coroutine.resume() with infinite loop in child
34+
function testcoroutine()
35+
local co = coroutine.create(infiniteloop)
36+
local success, err = coroutine.resume(co)
37+
-- Should never reach here - kill should propagate to parent
38+
error("should not reach here")
39+
end
40+
41+
-- Test nested coroutines with infinite loop
42+
function testnestedcoroutine()
43+
local inner = coroutine.create(infiniteloop)
44+
local outer = coroutine.create(function()
45+
coroutine.resume(inner)
46+
error("should not reach here")
47+
end)
48+
coroutine.resume(outer)
49+
error("should not reach here")
50+
end
51+
52+
-- Test coroutine.wrap() with infinite loop
53+
function testwrap()
54+
local f = coroutine.wrap(infiniteloop)
55+
f()
56+
error("should not reach here")
57+
end
58+
3359
return "OK"

0 commit comments

Comments
 (0)