Why cglobal() in BigInt finalizer?

Looking at gmp.jl source, I noticed that the finalizer in BigInt definition uses cglobal to get the (I assume) function pointer:

function BigInt(; nbits::Integer=0)
        b = MPZ.init2!(new(), nbits)
        finalizer(cglobal((:__gmpz_clear, :libgmp)), b)
        return b

Why not use a ccall() or dlsym() there? What is the reasoning?

__gmpz_clear is a #defined alias for mpz_clear, which is a function: void mpz_clear (mpz_ptr);

However, when finalizer is called, it is called to register a finalization routine with (here b). It would be incorrect to call the finalization function here, so ccall is inappropriate.

cglobal Obtain a pointer to a global variable in a C-exported shared library, specified exactly as in ccall [ this is the (:__gmpz_clear, :libgmp) part]. Returns a Ptr{Type}, defaulting to Ptr{Cvoid} if no Type argument is supplied.

The values can be read or written by unsafe_load or unsafe_store!, respectively [ this is what we need to be stored as the finalizers’ information, because we need to load the ptr before calling the pointed_to_function – why, to keep garbage control doing the right thing, see: https://docs.julialang.org/en/v1.6-dev/base/base/#Base.GC.@preserve.

1 Like

Of course, it is inappropriate to call gmpz_clear() here, but why not pass to the finalizer the
anonymous function

x->ccall((:__gmpz_clear, :libgmp), Cvoid, (Ref{Mpz ,), x) ?

How would that be better?
also look at the init function in gmp.jl, part of it is

ccall((:__gmp_set_memory_functions, :libgmp), Cvoid,

The use of cglobal is consistent with the initialization of the memory functions. That is a good reason to use it in registering the finalizer (precludes some potentially inadvertent logical errors from occuring).