On Sat, Apr 16, 2016 at 6:55 PM, Andrei Zh <faithlessfri...@gmail.com> wrote:
> I have a question regarding Struct Type correspondences section of the
> documentation. Let's say, in a C library we have a struct like this:
>
> typedef struct {
>   int rows;
>   int cols;
>   double* data;
> } xmatrix;
>
> In C code it can be created using function:
>
> int xmatrix_copy(void* src, xmatrix** mat, int rows, int cols);
>
> Note pointer-to-pointer in second argument. This C function allocates new
> region of memory for `xmatrix` and fills in the values (rows, cols and
> pointer to data). I call this function from Julia like this:
>
> type XMatrix
>   rows::Cint
>   cols::Cint
>   data::Ptr{Float64}
> end
>
>
> data = Float64[1, 2, 3, 4, 5, 6]
> matptr = Array(Ptr{XMatrix}, 1)
>
>
> ccall((:xmatrix_copy), Cint,
>         (Ptr{Void}, Ptr{Ptr{XMatrix}}, Cint, Cint),
>         pointer(data), matptr, Cint(3), Cint(2))
>
> mat = unsafe_load(matptr[1])

This is wrong, it leaks memory. See below.

>
> Details to notice in `ccall`:
>
> 1. In type tuple I set type of xmatrix argument to be a pointer-to-pointer
> to XMatrix.
> 2. As an argument, I pass 1-element array of pointers to XMatrix and let C
> code to fill it in.
>
> This approach works and I get initialized `XMatrix`. Yet, I don't really
> understand all details of memory management in this case, In particular:
>
> 1. Who owns memory of an instance of `XMatrix`? Is `unsafe_load` copying
> fields or just assigns Julia type tag to C-allocated region of memory?

The owner of the memory is determined by the c library. In this case
it seems that the C library owns the memory (which is generally the
case if the library provide new and free functions)
Julia never (and can never) assign tag to C memory. Unsafe load does
exactly what the name suggests and is equivalent to `*ptr` in C. It
copies the content of the struct and the return value
has no aliasing with the original pointer.

I assume the C library want the original pointer so you need to keep
the pointer in the wrapper type and define conversion functions to
pass it to C. See this[1] for an example for the proper way to do it
(mostly the ptr field, the cconvert and unsafe_convert definitions and
the finalizer(explained below)).

> 2. What would be the proper way to free allocated memory given that C
> library provides function `xmatrix_free(xmatrix* mat)`?

ccall the free function. You usually also want a finalizer on the
wrapper type mentioned above so that the GC can free the memory for
you when the julia object is collected. Note that you should use
finalizer to finalize object only for types that you don't care too
much about their lifetime since the time when a finalizer is called is
undefined.

> 3. Aforementioned section (and the next one) suggest to use `Ref{XMatrix}`
> instead. Can somebody provide an example of corresponding code using refs
> instead of wrapping into an array?

Ref on a bits type is basically a 0-dim array

matout = Ref{Ptr{XMatrix}}()

ccall((:xmatrix_copy), Cint,
        (Ptr{Void}, Ptr{Ptr{XMatrix}}, Cint, Cint),
        data, matout, Cint(3), Cint(2)) # DO NOT USE
`pointer(data)`!!!! This is the single most common mistake when using
ccall. It is fine in the global scope but if this is in a function,
julia is free to collect the data array when you are in/before
entering the ccall. See the manual section about cconvert and
unsafe_convert

matptr = matout[] # This is the C managed/owned pointer that you
should embed in your julia type.

>
> Thanks,
> Andrei


[1] 
https://github.com/yuyichao/LibArchive.jl/blob/4112772e8d1d7124c896bf98c5baee13149d3f8d/src/writer.jl#L28-L58

Reply via email to