lua-users home
lua-l archive

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

I have discovered that I can access a userdata from Lua after its
finalizer has run -- is this intended?  A test program at the bottom
of this email can demonstrate this on both Lua 5.2.1 and Lua 5.1.4.

I have always written __gc metamethods thinking that the value was
guaranteed to be inaccessible after __gc finishes.  The examples from
PIL (first edition) seem to assume this also, since they dereference
(without guards) pointers that are freed in __gc.  [0]

Looking at the reference manual, this is not explicitly promised, so
maybe it's just the responsibility of the __gc method to mark the object
destroyed, and for all other functions that operate on the userdata to
check that it is not already finalized?

As a related issue, it also appears that a value in a weak table is not
necessarily removed immediately after its finalizer is invoked.  This is
also demonstrated in the attached program.

I can work around all of this, of course, I was just wondering whether
this behavior is as intended, and if so if it can be documented more
explicitly (and the code examples in PIL fixed to indicate this).


[0] See dir_iter():  (Sorry I
couldn't also check the 2nd edition, I can't find my copy at the


#include "lauxlib.h"
#include <stdio.h>

#if LUA_VERSION_NUM == 501
#define setfuncs(L, l) luaL_register(L, NULL, l)
#define luaL_setmetatable(L, name) \
  luaL_getmetatable(L, name); \
  lua_setmetatable(L, -2)
#elif LUA_VERSION_NUM == 502
#define setfuncs(L, l) luaL_setfuncs(L, l, 0)

static const char *mytype = "mytype";

static int newobj(lua_State *L) {
  void *ud = lua_newuserdata(L, 0);
  printf("new userdata=%p\n", ud);
  luaL_setmetatable(L, mytype);
  return 1;

static int call(lua_State *L) {
  printf("__call userdata=%p\n", luaL_checkudata(L, 1, mytype));
  return 0;

static int gc(lua_State *L) {
  printf("__gc userdata=%p\n", luaL_checkudata(L, 1, mytype));
  return 0;

static const struct luaL_Reg mm[] = {
  {"__gc", gc},
  {"__call", call},

int luaopen_ext(lua_State *L) {
  luaL_newmetatable(L, mytype);
  setfuncs(L, mm);
  lua_pushcfunction(L, &newobj);
  return 1;


local ext = require "ext"

if _VERSION >= 'Lua 5.2' then
  function defer(fn)
    setmetatable({}, { __gc = fn })
  function defer(fn)
    getmetatable(newproxy(true)).__gc = fn

local x
local y = {}
setmetatable(y, {__mode = "v"})
x = ext()
y[1] = ext()



new userdata=0x634c88
new userdata=0x634cb8
__gc userdata=0x634cb8
__gc userdata=0x634c88
__call userdata=0x634c88
__call userdata=0x634cb8