Re: another small wish

  • From: Mike Pall <mike-1205@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Wed, 30 May 2012 14:52:21 +0200

Cosmin Apreutesei wrote:
> The function bellow attempts to find the name of a constant in the
> winapi namespace given a prefix and a value.

I presume this is only used for debugging? Because this is rather
slow.

> It would be useful if cdata pointers would anchor their target if it's
> a struct or array. In Lua an unreachable object can be safely
> collected only because by definition it cannot affect any other
> object. Why not extend this useful semantic to ffi pointers?

I can see where you're coming from. But a core strength of the FFI
is that the GC _never_ has to traverse cdata types. It never has
to look for pointers or track ownership. In general, the GC cannot
know whether a pointer needs to be followed or not.

Ok, so one could remember that a certain pointer was originally
derived from a collectable object. But what happens if you do
p=p+42? Now you've got an interior pointer (which cannot be
followed) and the original 'p' is gone. Does that still keep the
GC object alive?

What happens with x.foo = ffi.new(...); x.bar = ffi.C.malloc(...)?
So the 'foo' pointer field needs to be followed, but the 'bar'
field doesn't. How do you want to efficiently keep this info?

Or what happens with p = ffi.C.strchr(str, 'x')? The str object
presumably needs to be kept alive or 'p' may be a dangling
pointer. There's no way the FFI can now that strchr() returns an
interior pointer to an object passed as an argument.

It's questions and inconsistencies like this, that make this
approach unattractive. It's much more consistent to never traverse
cdata objects and to shift the responsibility to keep GC objects
alive to the programmer. After all, this is a low-level API to
interface with C.

> Plus this property is so much taken for granted
> in Lua that it's easy to forget and write innocently looking code like
> SNDMSG(hwnd, BCM_GETIDEALSIZE, 0, ffi.cast('SIZE*', ffi.new('SIZE',
> size)))).

Yes, I realize there are some GC pitfalls with the FFI. But that's
kind of unavoidable if you mix manual memory management with a GC.

[BTW: That example doesn't make sense, because one needs to get
the returned button sizes from the struct _after_ the call returns.
So you have to keep a reference to the GC object, anyway.]

> In winapi there's tons of structs with short* buffers. In fbclient
> (firebird's client library) there's even structs with pointers to
> structs with pointers to arrays, all that you allocated yourself. I
> have two options: a) cram all related structs and buffers in tables
> and carry those around or b) use weak tables which gets me transparent
> anchoring (weak tables are underrated :)).

c) Allocate the outermost struct as a GC object, allocate all
other data by calling malloc() and provide a __gc metamethod for
the struct which calls free() on its members.

Remember: You don't _have_ to use GC objects. They aren't even the
best option if you have to do manual lifetime management, anyway.

> 3) issue
> ffi introspection (this has been asked before and probably answered,
> but I'll give my reasons), especially struct fields (name & type).
> 
> 3) story
> Automatic testing (eg. code generation for unit tests stubs).
> Debugging tools (eg. pretty-print a struct).

Not sure if or when I'll add this. I'd like to keep this outside
of the core VM. Yes, the info is all there, tightly packed inside
some internal data structures.

I can imagine a 'ffi.reflect' module, which accesses those
internals (using the FFI itself!) and offers a nice API with type
info, type iterators, field iterators and the like.

[Alas, I don't have the time to design or implement this right now.]

--Mike

Other related posts: