diff options
| -rw-r--r-- | ldo.c | 20 | ||||
| -rw-r--r-- | lgc.c | 2 | ||||
| -rw-r--r-- | lstate.c | 17 | ||||
| -rw-r--r-- | lstate.h | 2 | ||||
| -rw-r--r-- | ltests.c | 23 | ||||
| -rw-r--r-- | testes/gc.lua | 42 | ||||
| -rw-r--r-- | testes/memerr.lua | 19 | ||||
| -rw-r--r-- | testes/tracegc.lua | 9 |
8 files changed, 118 insertions, 16 deletions
| @@ -221,13 +221,21 @@ l_noret luaD_errerr (lua_State *L) { | |||
| 221 | 221 | ||
| 222 | 222 | ||
| 223 | /* | 223 | /* |
| 224 | ** Check whether stack has enough space to run a simple function (such | 224 | ** Check whether stacks have enough space to run a simple function (such |
| 225 | ** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack and | 225 | ** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two |
| 226 | ** 2 slots in the C stack. | 226 | ** available CallInfos, and two "slots" in the C stack. |
| 227 | */ | 227 | */ |
| 228 | int luaD_checkminstack (lua_State *L) { | 228 | int luaD_checkminstack (lua_State *L) { |
| 229 | return ((stacksize(L) < MAXSTACK - BASIC_STACK_SIZE) && | 229 | if (getCcalls(L) >= LUAI_MAXCCALLS - 2) |
| 230 | (getCcalls(L) < LUAI_MAXCCALLS - 2)); | 230 | return 0; /* not enough C-stack slots */ |
| 231 | if (L->ci->next == NULL && luaE_extendCI(L, 0) == NULL) | ||
| 232 | return 0; /* unable to allocate first ci */ | ||
| 233 | if (L->ci->next->next == NULL && luaE_extendCI(L, 0) == NULL) | ||
| 234 | return 0; /* unable to allocate second ci */ | ||
| 235 | if (L->stack_last.p - L->top.p >= BASIC_STACK_SIZE) | ||
| 236 | return 1; /* enough (BASIC_STACK_SIZE) free slots in the Lua stack */ | ||
| 237 | else /* try to grow stack to a size with enough free slots */ | ||
| 238 | return luaD_growstack(L, BASIC_STACK_SIZE, 0); | ||
| 231 | } | 239 | } |
| 232 | 240 | ||
| 233 | 241 | ||
| @@ -616,7 +624,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
| 616 | 624 | ||
| 617 | 625 | ||
| 618 | 626 | ||
| 619 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) | 627 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1)) |
| 620 | 628 | ||
| 621 | 629 | ||
| 622 | /* | 630 | /* |
| @@ -1293,7 +1293,7 @@ static void finishgencycle (lua_State *L, global_State *g) { | |||
| 1293 | correctgraylists(g); | 1293 | correctgraylists(g); |
| 1294 | checkSizes(L, g); | 1294 | checkSizes(L, g); |
| 1295 | g->gcstate = GCSpropagate; /* skip restart */ | 1295 | g->gcstate = GCSpropagate; /* skip restart */ |
| 1296 | if (!g->gcemergency && luaD_checkminstack(L)) | 1296 | if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L)) |
| 1297 | callallpendingfinalizers(L); | 1297 | callallpendingfinalizers(L); |
| 1298 | } | 1298 | } |
| 1299 | 1299 | ||
| @@ -68,14 +68,19 @@ void luaE_setdebt (global_State *g, l_mem debt) { | |||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | 70 | ||
| 71 | CallInfo *luaE_extendCI (lua_State *L) { | 71 | CallInfo *luaE_extendCI (lua_State *L, int err) { |
| 72 | CallInfo *ci; | 72 | CallInfo *ci; |
| 73 | lua_assert(L->ci->next == NULL); | 73 | ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo); |
| 74 | ci = luaM_new(L, CallInfo); | 74 | if (l_unlikely(ci == NULL)) { /* allocation failed? */ |
| 75 | lua_assert(L->ci->next == NULL); | 75 | if (err) |
| 76 | L->ci->next = ci; | 76 | luaM_error(L); /* raise the error */ |
| 77 | return NULL; /* else only report it */ | ||
| 78 | } | ||
| 79 | ci->next = L->ci->next; | ||
| 77 | ci->previous = L->ci; | 80 | ci->previous = L->ci; |
| 78 | ci->next = NULL; | 81 | L->ci->next = ci; |
| 82 | if (ci->next) | ||
| 83 | ci->next->previous = ci; | ||
| 79 | ci->u.l.trap = 0; | 84 | ci->u.l.trap = 0; |
| 80 | L->nci++; | 85 | L->nci++; |
| 81 | return ci; | 86 | return ci; |
| @@ -438,7 +438,7 @@ union GCUnion { | |||
| 438 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); | 438 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); |
| 439 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); | 439 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); |
| 440 | LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); | 440 | LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); |
| 441 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); | 441 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err); |
| 442 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); | 442 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); |
| 443 | LUAI_FUNC void luaE_checkcstack (lua_State *L); | 443 | LUAI_FUNC void luaE_checkcstack (lua_State *L); |
| 444 | LUAI_FUNC void luaE_incCstack (lua_State *L); | 444 | LUAI_FUNC void luaE_incCstack (lua_State *L); |
| @@ -1106,6 +1106,27 @@ static int stacklevel (lua_State *L) { | |||
| 1106 | } | 1106 | } |
| 1107 | 1107 | ||
| 1108 | 1108 | ||
| 1109 | static int resetCI (lua_State *L) { | ||
| 1110 | CallInfo *ci = L->ci; | ||
| 1111 | while (ci->next != NULL) { | ||
| 1112 | CallInfo *tofree = ci->next; | ||
| 1113 | ci->next = ci->next->next; | ||
| 1114 | luaM_free(L, tofree); | ||
| 1115 | L->nci--; | ||
| 1116 | } | ||
| 1117 | return 0; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | |||
| 1121 | static int reallocstack (lua_State *L) { | ||
| 1122 | int n = cast_int(luaL_checkinteger(L, 1)); | ||
| 1123 | lua_lock(L); | ||
| 1124 | luaD_reallocstack(L, cast_int(L->top.p - L->stack.p) + n, 1); | ||
| 1125 | lua_unlock(L); | ||
| 1126 | return 0; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | |||
| 1109 | static int table_query (lua_State *L) { | 1130 | static int table_query (lua_State *L) { |
| 1110 | const Table *t; | 1131 | const Table *t; |
| 1111 | int i = cast_int(luaL_optinteger(L, 2, -1)); | 1132 | int i = cast_int(luaL_optinteger(L, 2, -1)); |
| @@ -2182,6 +2203,8 @@ static const struct luaL_Reg tests_funcs[] = { | |||
| 2182 | {"s2d", s2d}, | 2203 | {"s2d", s2d}, |
| 2183 | {"sethook", sethook}, | 2204 | {"sethook", sethook}, |
| 2184 | {"stacklevel", stacklevel}, | 2205 | {"stacklevel", stacklevel}, |
| 2206 | {"resetCI", resetCI}, | ||
| 2207 | {"reallocstack", reallocstack}, | ||
| 2185 | {"sizes", get_sizes}, | 2208 | {"sizes", get_sizes}, |
| 2186 | {"testC", testC}, | 2209 | {"testC", testC}, |
| 2187 | {"makeCfunc", makeCfunc}, | 2210 | {"makeCfunc", makeCfunc}, |
diff --git a/testes/gc.lua b/testes/gc.lua index 62713dac..e50d9029 100644 --- a/testes/gc.lua +++ b/testes/gc.lua | |||
| @@ -707,4 +707,46 @@ end | |||
| 707 | 707 | ||
| 708 | collectgarbage(oldmode) | 708 | collectgarbage(oldmode) |
| 709 | 709 | ||
| 710 | |||
| 711 | if T then | ||
| 712 | print("testing stack issues when calling finalizers") | ||
| 713 | |||
| 714 | local X | ||
| 715 | local obj | ||
| 716 | |||
| 717 | local function initobj () | ||
| 718 | X = false | ||
| 719 | obj = setmetatable({}, {__gc = function () X = true end}) | ||
| 720 | end | ||
| 721 | |||
| 722 | local function loop (n) | ||
| 723 | if n > 0 then loop(n - 1) end | ||
| 724 | end | ||
| 725 | |||
| 726 | -- should not try to call finalizer without a CallInfo available | ||
| 727 | initobj() | ||
| 728 | loop(20) -- ensure stack space | ||
| 729 | T.resetCI() -- remove extra CallInfos | ||
| 730 | T.alloccount(0) -- cannot allocate more CallInfos | ||
| 731 | obj = nil | ||
| 732 | collectgarbage() -- will not call finalizer | ||
| 733 | T.alloccount() | ||
| 734 | assert(X == false) | ||
| 735 | collectgarbage() -- now will call finalizer (it was still pending) | ||
| 736 | assert(X == true) | ||
| 737 | |||
| 738 | -- should not try to call finalizer without stack space available | ||
| 739 | initobj() | ||
| 740 | loop(5) -- ensure enough CallInfos | ||
| 741 | T.reallocstack(0) -- remove extra stack slots | ||
| 742 | T.alloccount(0) -- cannot reallocate stack | ||
| 743 | obj = nil | ||
| 744 | collectgarbage() -- will not call finalizer | ||
| 745 | T.alloccount() | ||
| 746 | assert(X == false) | ||
| 747 | collectgarbage() -- now will call finalizer (it was still pending) | ||
| 748 | assert(X == true) | ||
| 749 | end | ||
| 750 | |||
| 751 | |||
| 710 | print('OK') | 752 | print('OK') |
diff --git a/testes/memerr.lua b/testes/memerr.lua index 9c940ca7..a55514a9 100644 --- a/testes/memerr.lua +++ b/testes/memerr.lua | |||
| @@ -282,6 +282,25 @@ testamem("growing stack", function () | |||
| 282 | return foo(100) | 282 | return foo(100) |
| 283 | end) | 283 | end) |
| 284 | 284 | ||
| 285 | |||
| 286 | collectgarbage() | ||
| 287 | collectgarbage() | ||
| 288 | global io, T, setmetatable, collectgarbage, print | ||
| 289 | |||
| 290 | local Count = 0 | ||
| 291 | testamem("finalizers", function () | ||
| 292 | local X = false | ||
| 293 | local obj = setmetatable({}, {__gc = function () X = true end}) | ||
| 294 | obj = nil | ||
| 295 | T.resetCI() -- remove extra CallInfos | ||
| 296 | T.reallocstack(18) -- remove extra stack slots | ||
| 297 | Count = Count + 1 | ||
| 298 | io.stderr:write(Count, "\n") | ||
| 299 | T.trick(io) | ||
| 300 | collectgarbage() | ||
| 301 | return X | ||
| 302 | end) | ||
| 303 | |||
| 285 | -- }================================================================== | 304 | -- }================================================================== |
| 286 | 305 | ||
| 287 | 306 | ||
diff --git a/testes/tracegc.lua b/testes/tracegc.lua index a8c929df..c1154f90 100644 --- a/testes/tracegc.lua +++ b/testes/tracegc.lua | |||
| @@ -1,10 +1,15 @@ | |||
| 1 | -- track collections | 1 | -- track collections |
| 2 | 2 | ||
| 3 | |||
| 3 | local M = {} | 4 | local M = {} |
| 4 | 5 | ||
| 5 | -- import list | 6 | -- import list |
| 6 | local setmetatable, stderr, collectgarbage = | 7 | local stderr, collectgarbage = io.stderr, collectgarbage |
| 7 | setmetatable, io.stderr, collectgarbage | 8 | |
| 9 | -- the debug version of setmetatable does not create any object (such as | ||
| 10 | -- a '__metatable' string), and so it is more appropriate to be used in | ||
| 11 | -- a finalizer | ||
| 12 | local setmetatable = require"debug".setmetatable | ||
| 8 | 13 | ||
| 9 | global none | 14 | global none |
| 10 | 15 | ||
