lua-users home
lua-l archive

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


On Tue, Sep 27, 2022 at 01:34:05AM +0100, ubq323 wrote:
> what is the purpose of the lua_setuservalue and lua_getuservalue
> (or in lua 5.4, getiuservalue and setiuservalue) functions?
> what's the intended use case of being able to associate arbitrary lua
> values with full userdata?
> and does anyone know of any notable or interesting uses of this
> functionality?

A userdata object may need to hold onto references of other Lua
objects--strings, tables, other userdata objects, etc, beyond the lifetime
of a function call. Lua provides several mechanisms for "anchoring" such
objects, the most obvious being the integer handles from luaL_ref. However,
Lua cannot detect reference cycles created through luaL_ref handles. In
other languages you either use an in-language proxy object, or use weak
references or ephemerons (weak tables and ephemeron tables, respectively, in
Lua). But those options introduce additional complexity of their own,
especially from C or other native code, as your application now has this
this extra proxying layer between every userdata object, or must juggle a
global store for the weak references or ephemerons. They also incur
performance costs: extra objects and function calls when proxying, or the
specialized GC treatment required for weak references and especially
ephemerons. Uservalues provide a purely local mechanism to reference objects
*through* the userdata object holding it, without resorting to proxies or
global tables. From the perspective of the GC, uservalue references behave
and are implemented just like other in-language reference; in particular,
they allow userdata objects retain references just as easily as an
in-language (i.e. table-based) object.

Before Lua 5.4 if you needed to anchor multiple objects then you needed to
create and assign a table to the uservalue slot. In that situation you were
incuring some of the costs as with proxying. With multiple uservalue slots,
you can usually avoid the extra table allocation and additional memory
churn--it's still just a single allocation, just of a larger size for the
additional slots.