Re: metatype __gc inconsistency

  • From: Mike Pall <mike-1409@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Fri, 19 Sep 2014 01:01:42 +0200

Wolfgang Pupp wrote:
> local typeof, sizeof = ffi.typeof, ffi.sizeof
> 
> local malloc
> do
>   local libc, fill, cast, gc = ffi.C, ffi.fill, ffi.cast, ffi.gc
>   malloc = function(ctype)
>     local ptr = cast(typeof('$ *', ctype), libc.malloc(sizeof(ctype)))
>     fill(ptr, sizeof(ctype))  --zero-fill
>     gc(ptr, libc.free)
>     return ptr
>   end
> end

Umm ...

1. Don't use ffi.typeof() in a fast path.

2. Be careful when creating parameterized types repeatedly. Ok, so
pointer types happen to be interned, but others aren't. If not
interned, it'll create a distinguished ctype every time. You don't
want that, since the JIT-compiler always specializes to a ctype.

3. Separating declaration and assignment of a local function kills
the immutable upvalue optimization (e.g. when it's called from
within another function after that). Don't do that.


The way to design such an API is to use a factory function:

  local C, cast, fill, gc = ffi.C, ffi.cast, ffi.fill, ffi.gc

  local function malloc_type(ct)
    local ctp, cts = ffi.typeof("$ *", ct), ffi.sizeof(ct)
    return function()
      local p = cast(ctp, C.malloc(cts))
      fill(p, cts)
      return gc(p, C.free)
    end
  end

  local point_new = malloc_type(PointF)

  local function whatever()
    local p1 = point_new()
    local p2 = point_new()
    ...
  end

But note you always pay for the allocation of the GC-managed
pointer object, anyway. Such a use of malloc() only makes sense if
the allocated types are very large. Otherwise it's just an arduous
ffi.new().

--Mike

Other related posts: