aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-06 12:09:44 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-06 12:09:44 -0300
commitb57574d6fb9071b2f8f261b32c9378ed72db7023 (patch)
tree41c6503788b696b6056c2d954da84843eebe321e
parentbfcf06d91a87b7ffb8c83e290db0cb6176a167f8 (diff)
downloadlua-b57574d6fb9071b2f8f261b32c9378ed72db7023.tar.gz
lua-b57574d6fb9071b2f8f261b32c9378ed72db7023.tar.bz2
lua-b57574d6fb9071b2f8f261b32c9378ed72db7023.zip
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.
-rw-r--r--lapi.c8
-rw-r--r--lauxlib.c6
-rw-r--r--lcorolib.c9
-rw-r--r--testes/api.lua75
4 files changed, 75 insertions, 23 deletions
diff --git a/lapi.c b/lapi.c
index bbba88a3..16eb170d 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1195,9 +1195,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
1195 1195
1196 1196
1197LUA_API int lua_error (lua_State *L) { 1197LUA_API int lua_error (lua_State *L) {
1198 TValue *errobj;
1198 lua_lock(L); 1199 lua_lock(L);
1200 errobj = s2v(L->top - 1);
1199 api_checknelems(L, 1); 1201 api_checknelems(L, 1);
1200 luaG_errormsg(L); 1202 /* error object is the memory error message? */
1203 if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
1204 luaM_error(L); /* raise a memory error */
1205 else
1206 luaG_errormsg(L); /* raise a regular error */
1201 /* code unreachable; will unlock when control actually leaves the kernel */ 1207 /* code unreachable; will unlock when control actually leaves the kernel */
1202 return 0; /* to avoid warnings */ 1208 return 0; /* to avoid warnings */
1203} 1209}
diff --git a/lauxlib.c b/lauxlib.c
index e3d9be37..cbe9ed31 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -475,8 +475,10 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
475 lua_Alloc allocf = lua_getallocf(L, &ud); 475 lua_Alloc allocf = lua_getallocf(L, &ud);
476 UBox *box = (UBox *)lua_touserdata(L, idx); 476 UBox *box = (UBox *)lua_touserdata(L, idx);
477 void *temp = allocf(ud, box->box, box->bsize, newsize); 477 void *temp = allocf(ud, box->box, box->bsize, newsize);
478 if (temp == NULL && newsize > 0) /* allocation error? */ 478 if (temp == NULL && newsize > 0) { /* allocation error? */
479 luaL_error(L, "not enough memory"); 479 lua_pushliteral(L, "not enough memory");
480 lua_error(L); /* raise a memory error */
481 }
480 box->box = temp; 482 box->box = temp;
481 box->bsize = newsize; 483 box->bsize = newsize;
482 return temp; 484 return temp;
diff --git a/lcorolib.c b/lcorolib.c
index 7d6e585b..c165031d 100644
--- a/lcorolib.c
+++ b/lcorolib.c
@@ -73,11 +73,12 @@ static int luaB_coresume (lua_State *L) {
73static int luaB_auxwrap (lua_State *L) { 73static int luaB_auxwrap (lua_State *L) {
74 lua_State *co = lua_tothread(L, lua_upvalueindex(1)); 74 lua_State *co = lua_tothread(L, lua_upvalueindex(1));
75 int r = auxresume(L, co, lua_gettop(L)); 75 int r = auxresume(L, co, lua_gettop(L));
76 if (r < 0) { 76 if (r < 0) { /* error? */
77 int stat = lua_status(co); 77 int stat = lua_status(co);
78 if (stat != LUA_OK && stat != LUA_YIELD) 78 if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
79 lua_resetthread(co); /* close variables in case of errors */ 79 lua_resetthread(co); /* close its tbc variables */
80 if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ 80 if (stat != LUA_ERRMEM && /* not a memory error and ... */
81 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
81 luaL_where(L, 1); /* add extra info, if available */ 82 luaL_where(L, 1); /* add extra info, if available */
82 lua_insert(L, -2); 83 lua_insert(L, -2);
83 lua_concat(L, 2); 84 lua_concat(L, 2);
diff --git a/testes/api.lua b/testes/api.lua
index 9447e42a..95551481 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -11,6 +11,9 @@ local debug = require "debug"
11local pack = table.pack 11local pack = table.pack
12 12
13 13
14-- standard error message for memory errors
15local MEMERRMSG = "not enough memory"
16
14function tcheck (t1, t2) 17function tcheck (t1, t2)
15 assert(t1.n == (t2.n or #t2) + 1) 18 assert(t1.n == (t2.n or #t2) + 1)
16 for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end 19 for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
@@ -408,7 +411,7 @@ do
408 411
409 -- memory error 412 -- memory error
410 T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) 413 T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
411 assert(T.checkpanic("newuserdata 20000") == "not enough memory") 414 assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
412 T.totalmem(0) -- restore high limit 415 T.totalmem(0) -- restore high limit
413 416
414 -- stack error 417 -- stack error
@@ -1153,40 +1156,74 @@ do
1153end 1156end
1154 1157
1155 1158
1156------------------------------------------------------------------------- 1159--[[
1157-- testing memory limits 1160** {==================================================================
1158------------------------------------------------------------------------- 1161** Testing memory limits
1162** ===================================================================
1163--]]
1164
1159print("memory-allocation errors") 1165print("memory-allocation errors")
1160 1166
1161checkerr("block too big", T.newuserdata, math.maxinteger) 1167checkerr("block too big", T.newuserdata, math.maxinteger)
1162collectgarbage() 1168collectgarbage()
1163local f = load"local a={}; for i=1,100000 do a[i]=i end" 1169local f = load"local a={}; for i=1,100000 do a[i]=i end"
1164T.alloccount(10) 1170T.alloccount(10)
1165checkerr("not enough memory", f) 1171checkerr(MEMERRMSG, f)
1166T.alloccount() -- remove limit 1172T.alloccount() -- remove limit
1167 1173
1174
1175-- test memory errors; increase limit for maximum memory by steps,
1176-- o that we get memory errors in all allocations of a given
1177-- task, until there is enough memory to complete the task without
1178-- errors.
1179function testbytes (s, f)
1180 collectgarbage()
1181 local M = T.totalmem()
1182 local oldM = M
1183 local a,b = nil
1184 while true do
1185 collectgarbage(); collectgarbage()
1186 T.totalmem(M)
1187 a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
1188 T.totalmem(0) -- remove limit
1189 if a and b == "OK" then break end -- stop when no more errors
1190 if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
1191 error(a, 0) -- propagate it
1192 end
1193 M = M + 7 -- increase memory limit
1194 end
1195 print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
1196 return a
1197end
1198
1168-- test memory errors; increase limit for number of allocations one 1199-- test memory errors; increase limit for number of allocations one
1169-- by one, so that we get memory errors in all allocations of a given 1200-- by one, so that we get memory errors in all allocations of a given
1170-- task, until there is enough allocations to complete the task without 1201-- task, until there is enough allocations to complete the task without
1171-- errors. 1202-- errors.
1172 1203
1173function testamem (s, f) 1204function testalloc (s, f)
1174 collectgarbage(); collectgarbage() 1205 collectgarbage()
1175 local M = 0 1206 local M = 0
1176 local a,b = nil 1207 local a,b = nil
1177 while true do 1208 while true do
1209 collectgarbage(); collectgarbage()
1178 T.alloccount(M) 1210 T.alloccount(M)
1179 a, b = pcall(f) 1211 a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
1180 T.alloccount() -- remove limit 1212 T.alloccount() -- remove limit
1181 if a and b then break end -- stop when no more errors 1213 if a and b == "OK" then break end -- stop when no more errors
1182 if not a and not -- `real' error? 1214 if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
1183 (string.find(b, "memory") or string.find(b, "overflow")) then 1215 error(a, 0) -- propagate it
1184 error(b, 0) -- propagate it
1185 end 1216 end
1186 M = M + 1 -- increase allocation limit 1217 M = M + 1 -- increase allocation limit
1187 end 1218 end
1188 print(string.format("limit for %s: %d allocations", s, M)) 1219 print(string.format("minimum allocations for %s: %d allocations", s, M))
1189 return b 1220 return a
1221end
1222
1223
1224local function testamem (s, f)
1225 testalloc(s, f)
1226 return testbytes(s, f)
1190end 1227end
1191 1228
1192 1229
@@ -1196,8 +1233,11 @@ assert(b == 10)
1196 1233
1197-- testing memory errors when creating a new state 1234-- testing memory errors when creating a new state
1198 1235
1199b = testamem("state creation", T.newstate) 1236testamem("state creation", function ()
1200T.closestate(b); -- close new state 1237 local st = T.newstate()
1238 if st then T.closestate(st) end -- close new state
1239 return st
1240end)
1201 1241
1202testamem("empty-table creation", function () 1242testamem("empty-table creation", function ()
1203 return {} 1243 return {}
@@ -1345,6 +1385,9 @@ testamem("growing stack", function ()
1345 return foo(100) 1385 return foo(100)
1346end) 1386end)
1347 1387
1388-- }==================================================================
1389
1390
1348do -- testing failing in 'lua_checkstack' 1391do -- testing failing in 'lua_checkstack'
1349 local res = T.testC([[rawcheckstack 500000; return 1]]) 1392 local res = T.testC([[rawcheckstack 500000; return 1]])
1350 assert(res == false) 1393 assert(res == false)