[MLton-devel] Re: [MLton-user] ffi pointer lifetime
Matthew Fluet
fluet@cs.cornell.edu
Tue, 13 May 2003 09:21:13 -0400 (EDT)
Note, however, that finalization won't help in Vesa's case, since his
values are Word32.word. (Or, rather, he will see the finalization code
run after the next garbage collection and the C-side datastructures will
be free-ed too early.) Furthermore, I don't think that
datatype cptr = CPtr of Word32.word ref
local
val allocC = _ffi "alloc function": unit -> Word32.word;
val freeC = _ffi "free function": Word32.word -> unit;
in
fun alloc () : cptr =
let
val w = allocC ()
val ptr = CPtr (ref w)
in
MLton.Finalize.finalize (ptr, fn () => freeC w)
; ptr
end
end
is sufficient, because MLton (in particular, local-ref) could eliminate
the ref-cell. I think the following hack would fool MLton (well, fooling
is probably too strong, as MLton never tries to figure out what's
happening on the C-side):
type cptr = Word32.word ref
local
val allocC = _ffi "alloc function": unit -> Word32.word;
val nopC = _ffi "nop function": Word32.word ref -> unit;
val freeC = _ffi "free function": Word32.word -> unit;
in
fun alloc () : cptr =
let
val w = allocC ()
val ptr = ref w
in
nopC ptr
; MLton.Finalize.finalize (ptr, fn () => freeC w)
; ptr
end
end
In any event, I think this will be the common case: C-functions that yield
(malloc-ed) pointers which want to be finalized by calling a free
function. We should have a model of the "right way" to do this and add it
to the user's guide. (I'm not sure that the above or below is necessarily
the right model.)
This reminds me of something else on finalization: I think that we should
use List.foldr instead of List.foldl in the handleGC function for
MLton.finalize. The rationale is that foldr would execute finalizers in
the order in which they were installed (which can be important on the
C-side) and maintains the order of the to-be-finalized list.
The importance of executing finalizers in the right order on the C-side is
that we were a little naive above with cptr. A better implementation
might be the following:
local
val nopC = _ffi "nop function": Word32.word -> unit
in
datatype cptr = CPtr of Word32.word * cptr list
fun getW (CPtr (w, _)) = w
fun getPtrs (CPtr (_, ptrs)) = ptrs
fun make (w, ptrs) =
(List.app ((List.app (nopC o getW)) o getPtrs) ptrs
; CPtr (w, ptrs))
end
local
val allocC = _ffi "alloc function": unit -> Word32.word;
val linkC = _ffi "link function": Word32.word -> Word32.word;
val nopC = _ffi "nop function": Word32.word ref -> unit;
val freeC = _ffi "free function": Word32.word -> unit;
in
fun alloc () : cptr =
let
val w = allocC ()
val ptr = make (w, [])
in
MLton.Finalize.finalize (ptr, fn () => freeC w)
; ptr
end
fun link (ptr': cptr) : cptr =
let
val w = linkC (getW ptr')
val ptr = make (w, [ptr'])
in
MLton.Finalize.finalize (ptr, fn () => freeC w)
; ptr
end
end
That is, we explicitly maintain (an abstraction) of the C-side pointer
structure, thereby suspending the finalization of indirectly used C
datastructures. Furthermore, if we execute finalizations in order of
installation, then we will always free a link before freeing the pointers
it uses (which may be important for some C libraries). Unfortunately, I'm
not sure MLton won't over optimize -- I've tried to make the make function
"complicated" enough so that MLton will leave cptr as a tuple which could
be tracked by finalization, but I'm not sure how to guarantee that that
will be the case. If you inline all calls to make, I could see MLton
optimizing to the point that cptr is just the Word32.word, which would
defeat finalization.
In some sense, MLton.Weak.new seems to suffer from some of the same
problems of MLton.size.
-------------------------------------------------------
Enterprise Linux Forum Conference & Expo, June 4-6, 2003, Santa Clara
The only event dedicated to issues related to Linux enterprise solutions
www.enterpriselinuxforum.com
_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel