mirror of https://github.com/rusefi/lua.git
Keep memory errors as memory errors
Allow memory errors to be raised through the API (throwing the error with the memory error message); error in external allocations raises a memory error; memory errors in coroutines are re-raised as memory errors.
This commit is contained in:
parent
bfcf06d91a
commit
b57574d6fb
8
lapi.c
8
lapi.c
|
@ -1195,9 +1195,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_error (lua_State *L) {
|
LUA_API int lua_error (lua_State *L) {
|
||||||
|
TValue *errobj;
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
|
errobj = s2v(L->top - 1);
|
||||||
api_checknelems(L, 1);
|
api_checknelems(L, 1);
|
||||||
luaG_errormsg(L);
|
/* error object is the memory error message? */
|
||||||
|
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
|
||||||
|
luaM_error(L); /* raise a memory error */
|
||||||
|
else
|
||||||
|
luaG_errormsg(L); /* raise a regular error */
|
||||||
/* code unreachable; will unlock when control actually leaves the kernel */
|
/* code unreachable; will unlock when control actually leaves the kernel */
|
||||||
return 0; /* to avoid warnings */
|
return 0; /* to avoid warnings */
|
||||||
}
|
}
|
||||||
|
|
|
@ -475,8 +475,10 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
||||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||||
UBox *box = (UBox *)lua_touserdata(L, idx);
|
UBox *box = (UBox *)lua_touserdata(L, idx);
|
||||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
||||||
if (temp == NULL && newsize > 0) /* allocation error? */
|
if (temp == NULL && newsize > 0) { /* allocation error? */
|
||||||
luaL_error(L, "not enough memory");
|
lua_pushliteral(L, "not enough memory");
|
||||||
|
lua_error(L); /* raise a memory error */
|
||||||
|
}
|
||||||
box->box = temp;
|
box->box = temp;
|
||||||
box->bsize = newsize;
|
box->bsize = newsize;
|
||||||
return temp;
|
return temp;
|
||||||
|
|
|
@ -73,11 +73,12 @@ static int luaB_coresume (lua_State *L) {
|
||||||
static int luaB_auxwrap (lua_State *L) {
|
static int luaB_auxwrap (lua_State *L) {
|
||||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||||
int r = auxresume(L, co, lua_gettop(L));
|
int r = auxresume(L, co, lua_gettop(L));
|
||||||
if (r < 0) {
|
if (r < 0) { /* error? */
|
||||||
int stat = lua_status(co);
|
int stat = lua_status(co);
|
||||||
if (stat != LUA_OK && stat != LUA_YIELD)
|
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
||||||
lua_resetthread(co); /* close variables in case of errors */
|
lua_resetthread(co); /* close its tbc variables */
|
||||||
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
|
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||||
|
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||||
luaL_where(L, 1); /* add extra info, if available */
|
luaL_where(L, 1); /* add extra info, if available */
|
||||||
lua_insert(L, -2);
|
lua_insert(L, -2);
|
||||||
lua_concat(L, 2);
|
lua_concat(L, 2);
|
||||||
|
|
|
@ -11,6 +11,9 @@ local debug = require "debug"
|
||||||
local pack = table.pack
|
local pack = table.pack
|
||||||
|
|
||||||
|
|
||||||
|
-- standard error message for memory errors
|
||||||
|
local MEMERRMSG = "not enough memory"
|
||||||
|
|
||||||
function tcheck (t1, t2)
|
function tcheck (t1, t2)
|
||||||
assert(t1.n == (t2.n or #t2) + 1)
|
assert(t1.n == (t2.n or #t2) + 1)
|
||||||
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
|
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
|
||||||
|
@ -408,7 +411,7 @@ do
|
||||||
|
|
||||||
-- memory error
|
-- memory error
|
||||||
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
||||||
assert(T.checkpanic("newuserdata 20000") == "not enough memory")
|
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
|
||||||
T.totalmem(0) -- restore high limit
|
T.totalmem(0) -- restore high limit
|
||||||
|
|
||||||
-- stack error
|
-- stack error
|
||||||
|
@ -1153,40 +1156,74 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
--[[
|
||||||
-- testing memory limits
|
** {==================================================================
|
||||||
-------------------------------------------------------------------------
|
** Testing memory limits
|
||||||
|
** ===================================================================
|
||||||
|
--]]
|
||||||
|
|
||||||
print("memory-allocation errors")
|
print("memory-allocation errors")
|
||||||
|
|
||||||
checkerr("block too big", T.newuserdata, math.maxinteger)
|
checkerr("block too big", T.newuserdata, math.maxinteger)
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
local f = load"local a={}; for i=1,100000 do a[i]=i end"
|
local f = load"local a={}; for i=1,100000 do a[i]=i end"
|
||||||
T.alloccount(10)
|
T.alloccount(10)
|
||||||
checkerr("not enough memory", f)
|
checkerr(MEMERRMSG, f)
|
||||||
T.alloccount() -- remove limit
|
T.alloccount() -- remove limit
|
||||||
|
|
||||||
|
|
||||||
|
-- test memory errors; increase limit for maximum memory by steps,
|
||||||
|
-- o that we get memory errors in all allocations of a given
|
||||||
|
-- task, until there is enough memory to complete the task without
|
||||||
|
-- errors.
|
||||||
|
function testbytes (s, f)
|
||||||
|
collectgarbage()
|
||||||
|
local M = T.totalmem()
|
||||||
|
local oldM = M
|
||||||
|
local a,b = nil
|
||||||
|
while true do
|
||||||
|
collectgarbage(); collectgarbage()
|
||||||
|
T.totalmem(M)
|
||||||
|
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||||
|
T.totalmem(0) -- remove limit
|
||||||
|
if a and b == "OK" then break end -- stop when no more errors
|
||||||
|
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||||
|
error(a, 0) -- propagate it
|
||||||
|
end
|
||||||
|
M = M + 7 -- increase memory limit
|
||||||
|
end
|
||||||
|
print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
-- test memory errors; increase limit for number of allocations one
|
-- test memory errors; increase limit for number of allocations one
|
||||||
-- by one, so that we get memory errors in all allocations of a given
|
-- by one, so that we get memory errors in all allocations of a given
|
||||||
-- task, until there is enough allocations to complete the task without
|
-- task, until there is enough allocations to complete the task without
|
||||||
-- errors.
|
-- errors.
|
||||||
|
|
||||||
function testamem (s, f)
|
function testalloc (s, f)
|
||||||
collectgarbage(); collectgarbage()
|
collectgarbage()
|
||||||
local M = 0
|
local M = 0
|
||||||
local a,b = nil
|
local a,b = nil
|
||||||
while true do
|
while true do
|
||||||
|
collectgarbage(); collectgarbage()
|
||||||
T.alloccount(M)
|
T.alloccount(M)
|
||||||
a, b = pcall(f)
|
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||||
T.alloccount() -- remove limit
|
T.alloccount() -- remove limit
|
||||||
if a and b then break end -- stop when no more errors
|
if a and b == "OK" then break end -- stop when no more errors
|
||||||
if not a and not -- `real' error?
|
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||||
(string.find(b, "memory") or string.find(b, "overflow")) then
|
error(a, 0) -- propagate it
|
||||||
error(b, 0) -- propagate it
|
|
||||||
end
|
end
|
||||||
M = M + 1 -- increase allocation limit
|
M = M + 1 -- increase allocation limit
|
||||||
end
|
end
|
||||||
print(string.format("limit for %s: %d allocations", s, M))
|
print(string.format("minimum allocations for %s: %d allocations", s, M))
|
||||||
return b
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function testamem (s, f)
|
||||||
|
testalloc(s, f)
|
||||||
|
return testbytes(s, f)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1196,8 +1233,11 @@ assert(b == 10)
|
||||||
|
|
||||||
-- testing memory errors when creating a new state
|
-- testing memory errors when creating a new state
|
||||||
|
|
||||||
b = testamem("state creation", T.newstate)
|
testamem("state creation", function ()
|
||||||
T.closestate(b); -- close new state
|
local st = T.newstate()
|
||||||
|
if st then T.closestate(st) end -- close new state
|
||||||
|
return st
|
||||||
|
end)
|
||||||
|
|
||||||
testamem("empty-table creation", function ()
|
testamem("empty-table creation", function ()
|
||||||
return {}
|
return {}
|
||||||
|
@ -1345,6 +1385,9 @@ testamem("growing stack", function ()
|
||||||
return foo(100)
|
return foo(100)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- }==================================================================
|
||||||
|
|
||||||
|
|
||||||
do -- testing failing in 'lua_checkstack'
|
do -- testing failing in 'lua_checkstack'
|
||||||
local res = T.testC([[rawcheckstack 500000; return 1]])
|
local res = T.testC([[rawcheckstack 500000; return 1]])
|
||||||
assert(res == false)
|
assert(res == false)
|
||||||
|
|
Loading…
Reference in New Issue