[ocaml-ctypes] Help with probable GC problem

Andre Nathan andre at digirati.com.br
Wed Dec 20 01:21:09 GMT 2017


Jeremy, it appears you are right, as usual.

I've replace my type definition:

   type 'm connection = B.mysql

with

   type 'm connection =
     { handle : B.mysql
     ; host   : char Ctypes.ptr option
     ; port   : int
     ; user   : char Ctypes.ptr option
     ; pass   : char Ctypes.ptr option
     ; db     : char Ctypes.ptr option
     ; socket : char Ctypes.ptr option
     }

and that made the error disappear (initially the record only had a field 
for the user, which caused and error on the password, then on the db 
name, etc. which I guess confirms your diagnosis). The port is there 
only for completeness.

Tomorrow I'll make the fix in the real library code.

Regarding ocaml-memcpy, would it have the same effect in this case if I 
replaced the "ptr_opt char" parameters in the binding specifications 
with just "string", thus letting ctypes make the copy? In that case, 
would I still need to keep a reference to the strings on the OCaml side?

Cheers, and thanks a lot,
Andre


On 12/19/2017 09:55 PM, Jeremy Yallop wrote:
> Dear Andre,
> 
> On 19 December 2017 at 23:00, Andre Nathan <andre at digirati.com.br> wrote:
>> It seems like a memory value is being overwritten, or maybe claimed by the
>> OCaml GC, although in the library I explicitly copy the username string, and
>> libmariadb calls strdup() on it so I'm not sure that's the problem.
>>
>> Since the library is very large, I created a branch with the minimal
>> bindings to open and close a connection, so that inspection of the code
>> becomes feasible:
>>
>>    https://github.com/andrenth/ocaml-mariadb/tree/minimal
> 
> Thanks for the clear explanation and reproduction case.  I think that
> your diagnosis is correct
> 
> It appears that the problem arises from the nonblocking nature of the
> code.  When a program creates a block of memory using Ctypes.allocate,
> like this
> 
>      let user = allocate ...
> 
> ctypes ensures the memory is not freed so long as 'user' is reachable.
> Additionally, if 'user' is passed to a C function then ctypes ensures
> that the memory is not freed for the duration of the function call.
> 
> However, in the MariaDB bindings 'user' appears to be accessed by
> MariaDB after the C function it's passed to returns.  More concretely,
> the pointer is passed into C like this:
> 
>      let user = allocate ... in
>      ...
>      B.mysql_real_connect_start ret mysql host user pass db port socket flags
> 
> and then mysql_real_connect_start keeps a reference to 'user' and
> returns immediately.  Since OCaml/ctypes can no longer see any uses of
> 'user', the memory is assumed to be no longer needed and so it's
> reclaimed by the GC.  However, MariaDB still has a reference (that it
> later accesses asynchronously) which is unsafe to use after the GC
> reclaims the memory.
> 
> If the above is correct then there are at least two solutions.  You
> could ensure somehow that 'user' and other such pointers are kept
> around on the OCaml side until the associated memory is no longer
> needed by MariaDB.  Or you could switch from 'allocate' to a
> malloc/free style arrangement to avoid the automatic collection
> altogether.
> 
> There's a bit of guesswork involved in the above, so please follow up
> if it doesn't fix the problem for you!
> 
> Kind regards,
> 
> Jeremy
> 
> PS You may find ocaml-memcpy (https://github.com/yallop/ocaml-memcpy)
> a useful alternative to char_ptr_buffer_of_string for copying strings
> between OCaml and C memory.
> 


More information about the Ctypes mailing list