lua-users home
lua-l archive

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


Hello.

I have a state in which several scripts have been loaded, resulting in,
among other things, the creation of various full userdata objects. For each
"type" of userdata I maintain in the registry a unicity table where the key
is a light userdata containing a pointer to the corresponding C object, and
the value is the associated lua full userdata (a box pointer in fact).
here is the function that registers an entry in the unicity table:

void pushBoxPointer ( lua_State * _l , int _ref_metaTable , int
_ref_unicityManager , void * _p , bool _needPrivateStorage )
{
  lua_pushnil ( _l ) ;                              // nil
  lua_getref ( _l , _ref_unicityManager ) ;         // nil UM
  lua_pushlightuserdata ( _l , _p ) ;               // nil UM _p
  lua_rawget ( _l , -2 ) ;                          // nil UM up|nil
  if ( lua_isnil ( _l , -1 ) )
  {                                                 // nil UM nil
    lua_pushlightuserdata ( _l , _p ) ;             // nil UM nil _p
    lua_boxpointer ( _l , _p ) ;                    // nil UM nil _p up
    lua_pushvalue ( _l , -1 ) ;                     // nil UM nil _p up up
    lua_replace ( _l , -6 ) ;                       // up  UM nil _p up
    lua_getref ( _l , _ref_metaTable ) ;            // up  UM nil _p up MT
    setMetatable ( _l , _needPrivateStorage ) ;     // up  UM nil _p up
    lua_rawset ( _l , -4 ) ;                        // up  UM nil
    lua_pop ( _l , 2 ) ;                            // up
  }
  else                                              // nil UM up
  {
    lua_replace ( _l , -3 ) ;                       // up UM
    lua_pop ( _l , 1 ) ;                            // up
  }
}

The userdata's metatable also contains a __gc field that references a C
closure to call at that point. This function, among other things, attempts
to remove the entry in the unicity table. 
here is the function that unregisters an entry in the unicity table:

void * unregisterBoxPointer ( lua_State * _l , int _ref_unicityManager , int
_index )
{
  bool error = true ;
  void * p = NULL ;
  if ( lua_type ( _l , _index ) == LUA_TUSERDATA )
  {
    // get the memory area of the userdata
    void * * userdata = static_cast< void * * > ( lua_touserdata ( _l ,
_index ) ) ;
    // get the value of the pointer it contains
    p = * userdata ;

    // make sure we don't attempt to unregister twice
    // (this might occur for example if an explicit method call clears the
userdata, and by the __gc method that kicks in and tries again)
    if ( p )
    {
      // and clear the contents of the userdata
      * userdata = NULL ;

      // remove the connexion between the pointer and the userdata in the
unicity table
      lua_getref ( _l , _ref_unicityManager ) ;         // UM
      lua_pushlightuserdata ( _l , p ) ;                // UM p
      lua_rawget ( _l , -2 ) ;                          // UM ud     << ###
MY PROBLEM IS HERE ###
      if ( ! lua_isnil ( _l , -1 ) )
      {
        lua_pop ( _l , 1 ) ;                            // UM
        lua_pushlightuserdata ( _l , p ) ;              // UM p
        lua_pushnil ( _l ) ;                            // UM p nil
        lua_rawset ( _l , -3 ) ;                        // UM
        lua_pop ( _l , 1 ) ;
        error = false ;
      }
      else                                              // UM nil
      {
        // what probably happened here is that we received an object of the
wrong LUA type
        lua_pop ( _l , 1 ) ;                            // UM
      }
    }
  }
  if ( error )
  {
    lua_getref ( _l , ref__name__ ) ;                   // UM "__name__"
    lua_rawget ( _l , -2 ) ;                            // UM __name__
    luaL_error ( _l , "Argument #%d is not a %s" , _index , lua_tostring (
_l , -1 ) ) ;
    lua_pop ( _l , 2 ) ;
  }
  return p ;
}


When the C object is created, it calls some function that creates the
corresponding lua object and registers it in the unicity table with the
function above.
Later on, another script is loaded via luaL_loadbuffer(), and my problems
begin:

Problem 1:
It happens that for one "type" I made a mistake , and the unicity table had
weak values. Also, a garbage collection cycle is triggered before the parser
is called. Since lua had no strong reference to the full userdata (that
nevertheless had a weak entry an entry in the faulty unicity table), the
full userdata is garbage collected, which is normal. My problem is that
unregisterBoxPointer(), called in the middle of the collection cycle, fails
to find the entry in the unicity table. Does it have something to do with
the way weak tables work ? Is it normal that the entry is silently removed
from my table at some point without my __gc metamethod being called ?

Problem 2:
Anyway, this causes unregisterBoxPointer() to raise an error. This errors
gets thrown, but is not protected by luaD_protectedparser (only the parsing
itself is protected), which causes my application to terminate, instead of
simply failing to load the script as I would have expected, and as would
occur in case of a syntax error. And in the event it would capture the
error, there is no means to specify an error handler function to call before
control is returned to the host, like lua_pcall() does. Could this be
changed for 5.0 final ?


Best regards,


Benoit.