[ocaml-ctypes] Lifetime of Ctypes allocation

Jeremy Yallop yallop at gmail.com
Mon Apr 18 10:50:45 BST 2016


Dear Markus,

On 16 April 2016 at 13:44, Markus Müller <llmarkvm at gmail.com> wrote:
> I'm having some trouble on how to properly control the lifetime of an
> OCaml allocated Ctypes "data structure". Consider the following simple
> C function.
>
> #include <stdio.h>
> void f(int * * p)
> {
>   printf("%d %d\n", *(p[0]), *(p[1]));
> }
>
> Using f in the way shown below is wrong since the memory pointed to by
> the inner pointers is freed by Gc.full_major.
>
> open Ctypes
> open Foreign
> let f = foreign "f" ((ptr (ptr int)) @-> returning void)
> let _ =
>   let v = allocate_n (ptr int) ~count:2 in
>   let _ = v <-@ (allocate int 0); (v +@ 1) <-@ (allocate int 1) in
>     f v ; Gc.full_major () ; f v
>
> The following crude attempt to fix it does not work either even though
> some references to the pointers are bound to global values.

The key here is that there are two types of memory:

   * OCaml-memory (memory containing OCaml values).  The OCaml garbage
collector knows about everything stored here.
   * C-memory (i.e. memory containing C values).  The GC doesn't know
anything about what's stored here.

For values created with 'allocate' there are two components:

   * the allocated memory itself lives in C-memory
   * the pointer returned by 'allocate' is an OCaml value that lives
in OCaml memory.

When there are no more references to the pointer then the allocated
memory will be freed.

With that in mind we can trace through what happens in your code.
I'll use the word 'address' for C addresses (which are probably just
numeric values) and 'pointer' for the OCaml objects of type Ctypes.ptr
which contain both addresses and other information for the GC and for
typing.

> let v = allocate_n (ptr int) ~count:2 in

This allocates a piece of C-memory large enough to hold two addresses.
The variable 'v' lives in OCaml-memory, and holds the pointer that
keeps the C-memory alive.

>     v <-@ (allocate int 0);

This allocates an piece of C-memory large enough to hold an integer,
initialized with 0.  The address of the allocated memory is written to
the C-memory referenced by 'v'.

At this point there are no references to the original pointer returned
by 'allocate', so the allocated memory may be freed.

>     a := (to_voidp (!@ v)) ;

This builds a pointer from the C-memory whose address is stored at
'v'.  There is no connection with the pointer returned by 'allocate',
besides the fact that both pointers store the same address.

I hope that helps you to build a mental model of what's going on, and
what's going wrong.  The main thing to remember is that in order to
keep a value alive you need to keep a reference to the original
pointer returned by 'allocate', not just the address of the C memory.
Here's a slight variant of the above code which does exactly that:

     let a = ref (from_voidp int null)
    a := allocate int 0 ;
    v <-@ !a;

Since 'a' holds the original pointer, not just the address of the
allocated C-memory, the GC won't collect the memory until 'a' is no
longer accessible.

Kind regards,

Jeremy.


More information about the Ctypes mailing list