Why this code does a segfault?

,

It’s Base.cconvert(Ref{mystruct}, st) working behind the scenes.

This is probably unsafe. The return value of Base.cconvert is only preserved from GC during this ccall: ccall((:updatestruct, "./libfoo.so"), Ref{mystruct}, (Ref{mystruct},), st). When the ccall returns, ret will store a pointer to the return value of Base.cconvert(Ref{mystruct}, st) and this value may be GC-ed at any time, so it’s not safe to use it in let_c_print_struct2.

If this code works fine, then it’s probably because 1. when Ref{mystruct} is used as a ccall return type, Julia can be notified to preserve a certain value to make its lifetime expands the returned Ref{{mystruct}; 2. you were so lucky that no GC got triggered between these two calls.

I’d say the de facto way of using Ref in ccall is:

function let_c_print_struct2(v_ref::Ref{mystruct})
    ccall((:recvstruct, "./libfoo.so"), Int32, (Ptr{mystruct},), v_ref)
end

function let_c_update_struct2()
    st_ref = Ref(mystruct(5, 6))
    ccall((:updatestruct, "./libfoo.so"), Ptr{mystruct}, (Ptr{mystruct},), st_ref)
    let_c_print_struct2(st_ref)
    st_ref
end

Now, there is no tmp variable that needs to be created when doing the conversion, so no GC-related segfaults.

5 Likes