Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions src/JlWrap/C.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ const PyJuliaBase_Type = Ref(C.PyNULL)
# we store the actual julia values here
# the `value` field of `PyJuliaValueObject` indexes into here
const PYJLVALUES = []
# unused indices in PYJLVALUES
# unused indices in PYJLVALUES; kept the same length as PYJLVALUES (extra slots
# are pushed alongside PYJLVALUES so the dealloc/finalizer path never has to
# resize this vector). Only the first PYJLFREEVALUECOUNT[] entries are valid
# free indices; the rest are placeholders.
const PYJLFREEVALUES = Int[]
const PYJLFREEVALUECOUNT = Ref(0)
# lock protecting PYJLVALUES, PYJLFREEVALUES and PYJLFREEVALUECOUNT
const PYJL_LOCK = Threads.SpinLock()

function _pyjl_new(t::C.PyPtr, ::C.PyPtr, ::C.PyPtr)
alloc = C.PyType_GetSlot(t, C.Py_tp_alloc)
Expand All @@ -39,8 +45,14 @@ end
function _pyjl_dealloc(o::C.PyPtr)
idx = C.@ft UnsafePtr{PyJuliaValueObject}(o).value[]
if idx != 0
# No allocation in this critical section: PYJLFREEVALUES has been pre-sized
# to match PYJLVALUES by PyJuliaValue_SetValue, so recording a free index is
# just a write to an existing slot plus a counter bump. This avoids the GC-triggered
lock(PYJL_LOCK)
PYJLVALUES[idx] = nothing
push!(PYJLFREEVALUES, idx)
PYJLFREEVALUECOUNT[] += 1
PYJLFREEVALUES[PYJLFREEVALUECOUNT[]] = idx
unlock(PYJL_LOCK)
end
(C.@ft UnsafePtr{PyJuliaValueObject}(o).weaklist[!]) == C.PyNULL || C.PyObject_ClearWeakRefs(o)
freeptr = C.PyType_GetSlot(C.Py_Type(o), C.Py_tp_free)
Expand Down Expand Up @@ -375,13 +387,19 @@ PyJuliaValue_SetValue(_o, @nospecialize(v)) = Base.GC.@preserve _o begin
o = C.asptr(_o)
idx = C.@ft UnsafePtr{PyJuliaValueObject}(o).value[]
if idx == 0
if isempty(PYJLFREEVALUES)
lock(PYJL_LOCK)
if PYJLFREEVALUECOUNT[] == 0
# Grow both vectors together so length(PYJLFREEVALUES) == length(PYJLVALUES)
# is preserved. The 0 in PYJLFREEVALUES is just a placeholder
push!(PYJLVALUES, v)
push!(PYJLFREEVALUES, 0)
idx = length(PYJLVALUES)
else
idx = pop!(PYJLFREEVALUES)
idx = PYJLFREEVALUES[PYJLFREEVALUECOUNT[]]
PYJLFREEVALUECOUNT[] -= 1
PYJLVALUES[idx] = v
end
unlock(PYJL_LOCK)
C.@ft UnsafePtr{PyJuliaValueObject}(o).value[] = idx
else
PYJLVALUES[idx] = v
Expand Down
Loading