@cfunction definition in a module/package


#1

For working with tools around libcairo (and other gui stuff) it’s necessary to provide callback functions in C-API. I’m looking at a strange problem in front-porting from 0.6 to 1.0.

Within the package/module (CairoScript) i have

function proto_surface_create(v::Ptr{Nothing},c::Int32,w::Float64,h::Float64,uid::UInt64)
    s = CairoSurface(v) 
    s.ptr
end

surf_create_c = @cfunction(proto_surface_create, Ptr{Nothing}, 
     (Ptr{Nothing},Int32,Float64,Float64,UInt64))

but, when this surf_create_c is used in testing, i get an assertion of the underlying c code that this is 0x0000

julia> CairoScript.surf_create_c
Ptr{Nothing} @0x0000000000000000


julia: ../../../../../util/cairo-script/cairo-script-operators.c:6048: _surface: Assertion `hook != ((void *)0)' failed.

When doing the same on the command line, i get a valid ptr

julia> s1 = @cfunction(CairoScript.proto_surface_create, Ptr{Nothing}, 
            (Ptr{Nothing},Int32,Float64,Float64,UInt64))
Ptr{Nothing} @0x00007f52f05e9270

What do i miss here?


#2

Or maybe a better question: What has happened if cfunction returns a pointer to 0?


#3

It looks like your code is not actually safe for precompilation. You can read all about it here: https://docs.julialang.org/en/v1/manual/modules/index.html#Module-initialization-and-precompilation-1 but the general idea is that there’s no guarantee that a raw pointer (like a pointer to a c function) will be valid both when the module is precompiled and later on when the module is loaded, perhaps by an entirely different Julia process.

There is an example in the manual (in the section I linked) that shows an easy way of resolving the problem by changing:

surf_create_c = @cfunction(...)

into:

const surf_create_c = Ref{Ptr{Cvoid}}(0)

function __init__()
  surf_create_c[] = @cfunction(...)
end

This will cause the raw pointer to be computed when the module is loaded (i.e. when that pointer is valid) rather than when the module is precompiled. You just need to also change any usages of surf_create_c into surf_create_c[].

Note also that the cost declaration is not necessary for correctness here, but it will significantly improve the performance of any code that uses surf_create_c, since it avoids the performance penalty of non-const global variables.


#4

Thank you. I couldn’t test it by now, but it looks reasonable and is far away from what i assumed the solution to be.

Two questions:
Shouldn’t any cfunction only be resolved at initialization time?
Is it an issue of julia of not put out a warning, but just assign a 0?