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
end
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: Essentials · The Julia Language.
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,
(Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}),
cglobal(:jl_gc_counted_malloc),
cglobal(:jl_gc_counted_realloc_with_old_size),
cglobal(:jl_gc_counted_free_with_size))
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).