Calling C and Fortran Code (Julia documentation)

I have many questions about Memory Ownership in https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/.

One example, from this documentation we have:

function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real)
    if nmax < nmin
        throw(DomainError())
    end
    result_array = Vector{Cdouble}(undef, nmax - nmin + 1)
    errorcode = ccall(
        (:gsl_sf_bessel_Jn_array, :libgsl), # name of C function and library
        Cint,                               # output type
        (Cint, Cint, Cdouble, Ref{Cdouble}),# tuple of input types
        nmin, nmax, x, result_array         # names of Julia variables to pass in
    )
    if errorcode != 0
        error("GSL error code $errorcode")
    end
    return result_array
end

In this case Ref{Cdouble} is used and none of the unsafe functions are used. The result_array is created in Julia and passed to C thus I don’t have to copy anything.

But, String is used in a very different way in gethostname function:

function gethostname()
    hostname = Vector{UInt8}(undef, 128)
    ccall((:gethostname, "libc"), Int32,
          (Ptr{UInt8}, Csize_t),
          hostname, sizeof(hostname))
    hostname[end] = 0; # ensure null-termination
    return unsafe_string(pointer(hostname))
end

In this case one has to use unsafe_string(pointer(hostname)), but hostname vector was creade in Julia, similar to result_array. Why I have to copy the memory using unsafe function in this case? Maybe this is done just to truncate C string in NUL-terminated. But If I’m using Fortran I should use unsafe_string?
If I change unsafe_string(pointer(hostname)) to String(hostname) I end up with a String with \0 and some trash.

Also, why use Ptr{UInt8} not Ref{UInt8}? Using Ptr{UInt8} for this case will not implicate in memory leak?
After sf_bessel_Jn_array function has the following paragraf:

Note that for this code to work correctly, result_array must be declared to be of type Ref{Cdouble} and not Ptr{Cdouble} . The memory is managed by Julia and the Ref signature alerts Julia’s garbage collector to keep managing the memory for result_array while the ccall executes. If Ptr{Cdouble} were used instead, the ccall may still work, but Julia’s garbage collector would not be aware that the memory declared for result_array is being used by the external C function. As a result, the code may produce a memory leak if result_array never gets freed by the garbage collector, or if the garbage collector prematurely frees result_array , the C function may end up throwing an invalid memory access exception.

1 Like

The implementation of gethostname is wrong. It needs to preserve the hostname array before the string is constructed.

The copying is simply because it was desired to free up the memory. Without copying you are stuck with the original allocation size.

Also, that paragraph about ptr vs ref for gsl is completely wrong and should be removed immediately. I knew there was some confusing statements in the doc that was causing ppl to misunderstand even though they are correct. I didn’t know this one is there though… This paragraph is just wrong in so many ways and there isn’t a way to interprete it in a way that’s actually correct.

The doc was fixed in this PR. Now, that paragraph is rephreased as:

The C function wrapped returns an integer error code; the results of the actual evaluation of the Bessel J function populate the Julia array result_array . This variable is declared as a Ref{Cdouble} , since its memory is allocated and managed by Julia. The implicit call to Base.cconvert(Ref{Cdouble}, result_array) unpacks the Julia pointer to a Julia array data structure into a form understandable by C.

Unfortunately there is no standard Fortran string ABI. I think gfortran attaches the size of the strings as extra arguments, e.g. see here:

1 Like