diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-06-12 10:31:38 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-06-12 10:31:38 -0300 |
| commit | 3cd9b56ae6002b4ef28d2467abd119606ae625d3 (patch) | |
| tree | ac8d7f3986a3b318ccd4e1b6c8e7661d199c0de7 | |
| parent | d2a9b4ffb86de29a201843edddfc0153a1846f96 (diff) | |
| download | lua-3cd9b56ae6002b4ef28d2467abd119606ae625d3.tar.gz lua-3cd9b56ae6002b4ef28d2467abd119606ae625d3.tar.bz2 lua-3cd9b56ae6002b4ef28d2467abd119606ae625d3.zip | |
Revamp around 'L->nCcalls' count
The field 'L->nCcalls' now counts downwards, so that the C-stack
limits do not depend on the stack size.
| -rw-r--r-- | ldo.c | 13 | ||||
| -rw-r--r-- | lstate.c | 53 | ||||
| -rw-r--r-- | lstate.h | 51 | ||||
| -rw-r--r-- | testes/all.lua | 14 |
4 files changed, 80 insertions, 51 deletions
| @@ -139,9 +139,8 @@ l_noret luaD_throw (lua_State *L, int errcode) { | |||
| 139 | 139 | ||
| 140 | 140 | ||
| 141 | int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { | 141 | int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { |
| 142 | l_uint32 oldnCcalls = L->nCcalls - L->nci; | 142 | l_uint32 oldnCcalls = L->nCcalls + L->nci; |
| 143 | struct lua_longjmp lj; | 143 | struct lua_longjmp lj; |
| 144 | lua_assert(L->nCcalls >= L->nci); | ||
| 145 | lj.status = LUA_OK; | 144 | lj.status = LUA_OK; |
| 146 | lj.previous = L->errorJmp; /* chain new error handler */ | 145 | lj.previous = L->errorJmp; /* chain new error handler */ |
| 147 | L->errorJmp = &lj; | 146 | L->errorJmp = &lj; |
| @@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { | |||
| 149 | (*f)(L, ud); | 148 | (*f)(L, ud); |
| 150 | ); | 149 | ); |
| 151 | L->errorJmp = lj.previous; /* restore old error handler */ | 150 | L->errorJmp = lj.previous; /* restore old error handler */ |
| 152 | L->nCcalls = oldnCcalls + L->nci; | 151 | L->nCcalls = oldnCcalls - L->nci; |
| 153 | return lj.status; | 152 | return lj.status; |
| 154 | } | 153 | } |
| 155 | 154 | ||
| @@ -521,7 +520,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) { | |||
| 521 | */ | 520 | */ |
| 522 | void luaD_callnoyield (lua_State *L, StkId func, int nResults) { | 521 | void luaD_callnoyield (lua_State *L, StkId func, int nResults) { |
| 523 | incXCcalls(L); | 522 | incXCcalls(L); |
| 524 | if (getCcalls(L) >= LUAI_MAXCSTACK) /* possible stack overflow? */ | 523 | if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */ |
| 525 | luaE_freeCI(L); | 524 | luaE_freeCI(L); |
| 526 | luaD_call(L, func, nResults); | 525 | luaD_call(L, func, nResults); |
| 527 | decXCcalls(L); | 526 | decXCcalls(L); |
| @@ -672,10 +671,10 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, | |||
| 672 | else if (L->status != LUA_YIELD) /* ended with errors? */ | 671 | else if (L->status != LUA_YIELD) /* ended with errors? */ |
| 673 | return resume_error(L, "cannot resume dead coroutine", nargs); | 672 | return resume_error(L, "cannot resume dead coroutine", nargs); |
| 674 | if (from == NULL) | 673 | if (from == NULL) |
| 675 | L->nCcalls = 1; | 674 | L->nCcalls = LUAI_MAXCSTACK; |
| 676 | else /* correct 'nCcalls' for this thread */ | 675 | else /* correct 'nCcalls' for this thread */ |
| 677 | L->nCcalls = getCcalls(from) - from->nci + L->nci + CSTACKCF; | 676 | L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF; |
| 678 | if (L->nCcalls >= LUAI_MAXCSTACK) | 677 | if (L->nCcalls <= CSTACKERR) |
| 679 | return resume_error(L, "C stack overflow", nargs); | 678 | return resume_error(L, "C stack overflow", nargs); |
| 680 | luai_userstateresume(L, nargs); | 679 | luai_userstateresume(L, nargs); |
| 681 | api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); | 680 | api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); |
| @@ -97,35 +97,34 @@ void luaE_setdebt (global_State *g, l_mem debt) { | |||
| 97 | 97 | ||
| 98 | 98 | ||
| 99 | /* | 99 | /* |
| 100 | ** Increment count of "C calls" and check for overflows. In case of | 100 | ** Decrement count of "C calls" and check for overflows. In case of |
| 101 | ** a stack overflow, check appropriate error ("regular" overflow or | 101 | ** a stack overflow, check appropriate error ("regular" overflow or |
| 102 | ** overflow while handling stack overflow). | 102 | ** overflow while handling stack overflow). If 'nCcalls' is smaller |
| 103 | ** If 'nCcalls' is larger than LUAI_MAXCSTACK but smaller than | 103 | ** than CSTACKERR but larger than CSTACKMARK, it means it has just |
| 104 | ** LUAI_MAXCSTACK + CSTACKCF (plus 2 to avoid by-one errors), it means | 104 | ** entered the "overflow zone", so the function raises an overflow |
| 105 | ** it has just entered the "overflow zone", so the function raises an | 105 | ** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is |
| 106 | ** overflow error. | 106 | ** already handling an overflow) but larger than CSTACKERRMARK, does |
| 107 | ** If 'nCcalls' is larger than LUAI_MAXCSTACK + CSTACKCF + 2 | 107 | ** not report an error (to allow message handling to work). Otherwise, |
| 108 | ** (which means it is already handling an overflow) but smaller than | 108 | ** report a stack overflow while handling a stack overflow (probably |
| 109 | ** 9/8 of LUAI_MAXCSTACK, does not report an error (to allow message | 109 | ** caused by a repeating error in the message handling function). |
| 110 | ** handling to work). | ||
| 111 | ** Otherwise, report a stack overflow while handling a stack overflow | ||
| 112 | ** (probably caused by a repeating error in the message handling | ||
| 113 | ** function). | ||
| 114 | */ | 110 | */ |
| 111 | |||
| 115 | void luaE_enterCcall (lua_State *L) { | 112 | void luaE_enterCcall (lua_State *L) { |
| 116 | int ncalls = getCcalls(L); | 113 | int ncalls = getCcalls(L); |
| 117 | L->nCcalls++; | 114 | L->nCcalls--; |
| 118 | if (ncalls >= LUAI_MAXCSTACK) { /* possible overflow? */ | 115 | if (ncalls <= CSTACKERR) { /* possible overflow? */ |
| 119 | luaE_freeCI(L); /* release unused CIs */ | 116 | luaE_freeCI(L); /* release unused CIs */ |
| 120 | ncalls = getCcalls(L); /* update call count */ | 117 | ncalls = getCcalls(L); /* update call count */ |
| 121 | if (ncalls >= LUAI_MAXCSTACK) { /* still overflow? */ | 118 | if (ncalls <= CSTACKERR) { /* still overflow? */ |
| 122 | if (ncalls <= LUAI_MAXCSTACK + CSTACKCF + 2) { | 119 | if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */ |
| 123 | /* no error before increments; raise the error now */ | ||
| 124 | L->nCcalls += (CSTACKCF + 4); /* avoid raising it again */ | ||
| 125 | luaG_runerror(L, "C stack overflow"); | ||
| 126 | } | ||
| 127 | else if (ncalls >= (LUAI_MAXCSTACK + (LUAI_MAXCSTACK >> 3))) | ||
| 128 | luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ | 120 | luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ |
| 121 | else if (ncalls >= CSTACKMARK) { | ||
| 122 | /* not in error-handling zone; raise the error now */ | ||
| 123 | L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */ | ||
| 124 | luaG_runerror(L, "C stack overflow1"); | ||
| 125 | } | ||
| 126 | /* else stack is in the error-handling zone; | ||
| 127 | allow message handler to work */ | ||
| 129 | } | 128 | } |
| 130 | } | 129 | } |
| 131 | } | 130 | } |
| @@ -153,13 +152,13 @@ void luaE_freeCI (lua_State *L) { | |||
| 153 | CallInfo *ci = L->ci; | 152 | CallInfo *ci = L->ci; |
| 154 | CallInfo *next = ci->next; | 153 | CallInfo *next = ci->next; |
| 155 | ci->next = NULL; | 154 | ci->next = NULL; |
| 156 | L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */ | 155 | L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ |
| 157 | while ((ci = next) != NULL) { | 156 | while ((ci = next) != NULL) { |
| 158 | next = ci->next; | 157 | next = ci->next; |
| 159 | luaM_free(L, ci); | 158 | luaM_free(L, ci); |
| 160 | L->nci--; | 159 | L->nci--; |
| 161 | } | 160 | } |
| 162 | L->nCcalls += L->nci; /* adjust result */ | 161 | L->nCcalls -= L->nci; /* adjust result */ |
| 163 | } | 162 | } |
| 164 | 163 | ||
| 165 | 164 | ||
| @@ -169,7 +168,7 @@ void luaE_freeCI (lua_State *L) { | |||
| 169 | void luaE_shrinkCI (lua_State *L) { | 168 | void luaE_shrinkCI (lua_State *L) { |
| 170 | CallInfo *ci = L->ci; | 169 | CallInfo *ci = L->ci; |
| 171 | CallInfo *next2; /* next's next */ | 170 | CallInfo *next2; /* next's next */ |
| 172 | L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */ | 171 | L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ |
| 173 | /* while there are two nexts */ | 172 | /* while there are two nexts */ |
| 174 | while (ci->next != NULL && (next2 = ci->next->next) != NULL) { | 173 | while (ci->next != NULL && (next2 = ci->next->next) != NULL) { |
| 175 | luaM_free(L, ci->next); /* free next */ | 174 | luaM_free(L, ci->next); /* free next */ |
| @@ -178,7 +177,7 @@ void luaE_shrinkCI (lua_State *L) { | |||
| 178 | next2->previous = ci; | 177 | next2->previous = ci; |
| 179 | ci = next2; /* keep next's next */ | 178 | ci = next2; /* keep next's next */ |
| 180 | } | 179 | } |
| 181 | L->nCcalls += L->nci; /* adjust result */ | 180 | L->nCcalls -= L->nci; /* adjust result */ |
| 182 | } | 181 | } |
| 183 | 182 | ||
| 184 | 183 | ||
| @@ -264,7 +263,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
| 264 | L->stacksize = 0; | 263 | L->stacksize = 0; |
| 265 | L->twups = L; /* thread has no upvalues */ | 264 | L->twups = L; /* thread has no upvalues */ |
| 266 | L->errorJmp = NULL; | 265 | L->errorJmp = NULL; |
| 267 | L->nCcalls = 0; | 266 | L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; |
| 268 | L->hook = NULL; | 267 | L->hook = NULL; |
| 269 | L->hookmask = 0; | 268 | L->hookmask = 0; |
| 270 | L->basehookcount = 0; | 269 | L->basehookcount = 0; |
| @@ -64,28 +64,45 @@ | |||
| 64 | 64 | ||
| 65 | /* | 65 | /* |
| 66 | ** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of | 66 | ** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of |
| 67 | ** how many "C calls" it can do in the C stack, to avoid C-stack overflow. | 67 | ** how many "C calls" it still can do in the C stack, to avoid C-stack |
| 68 | ** This count is very rough approximation; it considers only recursive | 68 | ** overflow. This count is very rough approximation; it considers only |
| 69 | ** functions inside the interpreter, as non-recursive calls can be | 69 | ** recursive functions inside the interpreter, as non-recursive calls |
| 70 | ** considered using a fixed (although unknown) amount of stack space. | 70 | ** can be considered using a fixed (although unknown) amount of stack |
| 71 | ** space. | ||
| 71 | ** | 72 | ** |
| 72 | ** The count itself has two parts: the lower part is the count itself; | 73 | ** The count has two parts: the lower part is the count itself; the |
| 73 | ** the higher part counts the number of non-yieldable calls in the stack. | 74 | ** higher part counts the number of non-yieldable calls in the stack. |
| 75 | ** (They are together so that we can change both with one instruction.) | ||
| 74 | ** | 76 | ** |
| 75 | ** Because calls to external C functions can use of unkown amount | 77 | ** Because calls to external C functions can use of unkown amount |
| 76 | ** of space (e.g., functions using an auxiliary buffer), calls | 78 | ** of space (e.g., functions using an auxiliary buffer), calls |
| 77 | ** to these functions add more than one to the count. | 79 | ** to these functions add more than one to the count (see CSTACKCF). |
| 78 | ** | 80 | ** |
| 79 | ** The proper count also includes the number of CallInfo structures | 81 | ** The proper count excludes the number of CallInfo structures allocated |
| 80 | ** allocated by Lua, as a kind of "potential" calls. So, when Lua | 82 | ** by Lua, as a kind of "potential" calls. So, when Lua calls a function |
| 81 | ** calls a function (and "consumes" one CallInfo), it needs neither to | 83 | ** (and "consumes" one CallInfo), it needs neither to decrement nor to |
| 82 | ** increment nor to check 'nCcalls', as its use of C stack is already | 84 | ** check 'nCcalls', as its use of C stack is already accounted for. |
| 83 | ** accounted for. | ||
| 84 | */ | 85 | */ |
| 85 | 86 | ||
| 86 | /* number of "C stack slots" used by an external C function */ | 87 | /* number of "C stack slots" used by an external C function */ |
| 87 | #define CSTACKCF 10 | 88 | #define CSTACKCF 10 |
| 88 | 89 | ||
| 90 | |||
| 91 | /* | ||
| 92 | ** The C-stack size is sliced in the following zones: | ||
| 93 | ** - larger than CSTACKERR: normal stack; | ||
| 94 | ** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow; | ||
| 95 | ** - [CSTACKCF, CSTACKERRMARK]: error-handling zone; | ||
| 96 | ** - below CSTACKERRMARK: buffer zone to signal overflow during overflow; | ||
| 97 | ** (Because the counter can be decremented CSTACKCF at once, we need | ||
| 98 | ** the so called "buffer zones", with at least that size, to properly | ||
| 99 | ** detect a change from one zone to the next.) | ||
| 100 | */ | ||
| 101 | #define CSTACKERR (8 * CSTACKCF) | ||
| 102 | #define CSTACKMARK (CSTACKERR - (CSTACKCF + 2)) | ||
| 103 | #define CSTACKERRMARK (CSTACKCF + 2) | ||
| 104 | |||
| 105 | |||
| 89 | /* true if this thread does not have non-yieldable calls in the stack */ | 106 | /* true if this thread does not have non-yieldable calls in the stack */ |
| 90 | #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) | 107 | #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) |
| 91 | 108 | ||
| @@ -99,11 +116,11 @@ | |||
| 99 | /* Decrement the number of non-yieldable calls */ | 116 | /* Decrement the number of non-yieldable calls */ |
| 100 | #define decnny(L) ((L)->nCcalls -= 0x10000) | 117 | #define decnny(L) ((L)->nCcalls -= 0x10000) |
| 101 | 118 | ||
| 102 | /* Increment the number of non-yieldable calls and nCcalls */ | 119 | /* Increment the number of non-yieldable calls and decrement nCcalls */ |
| 103 | #define incXCcalls(L) ((L)->nCcalls += 0x10000 + CSTACKCF) | 120 | #define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF) |
| 104 | 121 | ||
| 105 | /* Decrement the number of non-yieldable calls and nCcalls */ | 122 | /* Decrement the number of non-yieldable calls and increment nCcalls */ |
| 106 | #define decXCcalls(L) ((L)->nCcalls -= 0x10000 + CSTACKCF) | 123 | #define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF) |
| 107 | 124 | ||
| 108 | 125 | ||
| 109 | 126 | ||
| @@ -336,7 +353,7 @@ LUAI_FUNC void luaE_enterCcall (lua_State *L); | |||
| 336 | LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); | 353 | LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); |
| 337 | 354 | ||
| 338 | 355 | ||
| 339 | #define luaE_exitCcall(L) ((L)->nCcalls--) | 356 | #define luaE_exitCcall(L) ((L)->nCcalls++) |
| 340 | 357 | ||
| 341 | #endif | 358 | #endif |
| 342 | 359 | ||
diff --git a/testes/all.lua b/testes/all.lua index 2e6fe038..72121e8d 100644 --- a/testes/all.lua +++ b/testes/all.lua | |||
| @@ -95,6 +95,8 @@ local function F (m) | |||
| 95 | end | 95 | end |
| 96 | end | 96 | end |
| 97 | 97 | ||
| 98 | local Cstacklevel | ||
| 99 | |||
| 98 | local showmem | 100 | local showmem |
| 99 | if not T then | 101 | if not T then |
| 100 | local max = 0 | 102 | local max = 0 |
| @@ -104,6 +106,7 @@ if not T then | |||
| 104 | print(format(" ---- total memory: %s, max memory: %s ----\n", | 106 | print(format(" ---- total memory: %s, max memory: %s ----\n", |
| 105 | F(m), F(max))) | 107 | F(m), F(max))) |
| 106 | end | 108 | end |
| 109 | Cstacklevel = function () return 0 end -- no info about stack level | ||
| 107 | else | 110 | else |
| 108 | showmem = function () | 111 | showmem = function () |
| 109 | T.checkmemory() | 112 | T.checkmemory() |
| @@ -117,9 +120,16 @@ else | |||
| 117 | T.totalmem"string", T.totalmem"table", T.totalmem"function", | 120 | T.totalmem"string", T.totalmem"table", T.totalmem"function", |
| 118 | T.totalmem"userdata", T.totalmem"thread")) | 121 | T.totalmem"userdata", T.totalmem"thread")) |
| 119 | end | 122 | end |
| 123 | |||
| 124 | Cstacklevel = function () | ||
| 125 | local _, _, ncalls, nci = T.stacklevel() | ||
| 126 | return ncalls + nci -- number of free slots in the C stack | ||
| 127 | end | ||
| 120 | end | 128 | end |
| 121 | 129 | ||
| 122 | 130 | ||
| 131 | local Cstack = Cstacklevel() | ||
| 132 | |||
| 123 | -- | 133 | -- |
| 124 | -- redefine dofile to run files through dump/undump | 134 | -- redefine dofile to run files through dump/undump |
| 125 | -- | 135 | -- |
| @@ -211,6 +221,10 @@ debug.sethook(function (a) assert(type(a) == 'string') end, "cr") | |||
| 211 | -- to survive outside block | 221 | -- to survive outside block |
| 212 | _G.showmem = showmem | 222 | _G.showmem = showmem |
| 213 | 223 | ||
| 224 | |||
| 225 | assert(Cstack == Cstacklevel(), | ||
| 226 | "should be at the same C-stack level it was when started the tests") | ||
| 227 | |||
| 214 | end --) | 228 | end --) |
| 215 | 229 | ||
| 216 | local _G, showmem, print, format, clock, time, difftime, | 230 | local _G, showmem, print, format, clock, time, difftime, |
