lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> I was wondering what guarantees of exception safety[1] are provided in Lua.

We do extensive testing about the safety of Lua. When we compile Lua
in test mode, the allocation function (not luaM_realloc_) is redefined
so that it throws an error when the total memory reaches a limit. Then
we perform several tasks like this: we set this limit to memory_in_use
(so that any attempt to allocate more memory fails) and then go on
increasing it by 3 bytes each time, until we can complete the task. This
ensures that we have memory fails in practically every allocation point
in the code.


> To help in testing, the below patch to Lua prints "[r]" upon each
> allocation attempt and implements a function failalloc(n) that
> schedules a memory allocation failure in the next n-th allocation.
> This is used in the following example.  Here, g() has a memory
> allocation failure when trying to grow that stack in the OP_CALL.
> This causes the program to prints out that x is 2 despite the intended
> invariant that x == 1.
> 
>   local function g()
>     local x=1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20,
>             21,22,23,24,25,26,27,28
>   end
>   local x = 1
>   local function test()
>     x = x + 1
>     g()
>     x = x - 1
>   end
> 
>   failalloc(1)
>   local ok,msg = pcall(test)
>   failalloc(0)
> 
>   print(x, ok, msg)

This result is expected, is it not? "test" was interrupted by an
exception (memory allocation error, caught by pcall) after
incrementing x but before decrementing it. What value did you expect?


> Although the Lua Reference Manual[2] defines the behavior of stack
> growing for C functions, I don't think it does so for Lua functions.

The Lua stack is not visible for the users. Memory allocation errors
may occur almost anywhere inside a Lua script.


>  void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
>    global_State *g = G(L);
>    lua_assert((osize == 0) == (block == NULL));
> +  printf("[r]");
> +  if (g_failalloc && --g_failalloc == 0) { luaD_throw(L, LUA_ERRMEM); }
>    block = (*g->frealloc)(g->ud, block, osize, nsize);
>    if (block == NULL && nsize > 0)
>      luaD_throw(L, LUA_ERRMEM);

This patch is not correct: "Lua assumes that the allocator never fails
when osize >= nsize."  (http://www.lua.org/manual/5.1/manual.html#lua_Alloc)

-- Roberto