[ocaml-ctypes] Finalising data

Jeremy Yallop yallop at gmail.com
Sat May 17 00:52:13 BST 2014


Dear Florian,

On 30/04/2014, Florian Pichlmeier <florian.pichlmeier at mytum.de> wrote:
> i have this create function
>
> type t = unit ptr
> let zframe : t typ = ptr void
> let zframe_opt : t option typ = ptr_opt void
>
> let create msg =
>   let stub = foreign "zframe_new"
>       (string @-> size_t @-> returning zframe_opt)
>   in
>   let msg_size =  Size_t.of_int (String.length msg) in
>   match stub msg msg_size with
>   | None -> raise Frame_creation
>   | Some x -> x
>
> How can i tell the garbage collector to call my specific
> destroy function?

You can attach a finaliser, either to the zframe value itself, or to
another object which has the same lifetime as the zframe.  The
drawback of attaching the finaliser directly to the zframe is that a
number of ctypes functions (e.g. the functions for pointer arithmetic)
create new ptr values, so you may end up destroying the object while
you still have a pointer to it.  For example:

    let zf = create msg in
    let () = Gc.finalise destroy_zframe zf in
      (zf +@ 0)
    (* At this point you still have a pointer to the zframe you
       created, but the original Ctypes.ptr value has gone, so the
       GC is free to run the finaliser attached to it. *)

If you're already wrapping the zframe in a larger OCaml value, such as
a record, it would probably be wiser to attach the finaliser to that
value instead, since you can see more easily in your own code exactly
when copies are made.  If you want even stronger guarantees, you
should make sure that the type you use to wrap the zframe has a
mutable field, since the runtime is free to make copies of immutable
values.

It may be more advisable to consider an alternative interface that
makes the lifetime of your zframes deterministic and explicit.  One
simple approach is to follow the design of the channel interfaces in
the standard library, and expose a pair of functions

   val create : string -> t
   val destroy : t -> unit

then leave it up to the user to ensure that destroy_zframe is closed
at an appropriate moment (but perhaps catching other errors, such as
double closes).

An alternative approach is to follow the design often used in Scheme,
and expose a single higher-order function that manages the lifetime of
the frame.  For example, if you have a function with the following
interface

   val with_zframe : string -> (t -> 'a) -> 'a

then you might call it as follows

   with_zframe msg
     (fun zframe ->
         (* body: the zframe value is "live" here *)
     )

and the user can be confident that -- whether the body finishes
normally or with an exception -- with_zframe will destroy the value in
a timely way.  This approach is used in the Batteries library to
manage files: see with_file_in and with_file_out, for example:

   http://ocaml-batteries-team.github.io/batteries-included/hdoc2/BatFile.html#VALwith_file_in
   http://ocaml-batteries-team.github.io/batteries-included/hdoc2/BatFile.html#VALwith_file_out

I hope that helps,

Jeremy.


More information about the Ctypes mailing list