Here’s an example.
First the julia code to create a foreign DataType
:
function maketype(marker, mod::Module=@__MODULE__)
dtp = ccall(:jl_new_foreign_type,
Any,
(Symbol,Module,Any,Ptr{Cvoid}, Ptr{Cvoid}, Cint, Cint),
:QueueRef, mod, Any, marker, C_NULL, 1, 0)
mod.eval(
quote
const QueueRef = $dtp
# a constructor:
function QueueRef(h::Ptr)
# make a new QueueRef object
ptls = Core.getptls()
newobj = ccall(:jl_gc_alloc_typed, Any,
(Ptr{Cvoid}, Csize_t, Any,),
ptls, sizeof(Ptr{Cvoid}), QueueRef)::QueueRef
# store the pointer in it
unsafe_store!(Ptr{UInt}(pointer_from_objref(newobj)), UInt(h), 1)
# return the object
newobj
end
Base.getindex(qr::QueueRef) = unsafe_load(Ptr{Any}(pointer_from_objref(qr)))
end
)
end
Then some C-code which I compile to a shared library in the file libmarker.so
. This is where you loop through the C-queue and mark each entry.
#include <stdio.h>
#include "julia.h"
#include "julia_gcext.h"
int marker(jl_ptls_t ptls, jl_value_t *obj[]) {
jl_value_t *p = obj[0];
int marks = jl_gc_mark_queue_obj(ptls, p);
if(marks > 0) printf("marked %p %d\n",p,marks);
return marks;
}
Then a julia script using this stuff, which shows that I can lose all refs to the vector v
. A pointer is hidden inside the QueueRef
object, and the marker
C-function takes care of marking it as still in use:
using Libdl
flib = dlopen("./libmarker.so")
cfun = dlsym(flib,:marker)
maketype(cfun)
v = [42, 42, 42]
finalizer(v) do o
@ccall printf("finalize %p\n"::Cstring; pointer_from_objref(o)::Ptr{Nothing})::Cint
end
a = QueueRef(pointer_from_objref(v));
julia> a
QueueRef()
julia> v = nothing
julia> GC.gc(true)
marked 0x77e04e73f030 1
marked 0x77e04e73f030 1
julia> GC.gc(true)
marked 0x77e04e73f030 1
julia> a = nothing
julia> GC.gc(true)
finalize 0x77e04e73f030