diff --git a/benchmarks/pywrapfn.jl b/benchmarks/pywrapfn.jl index 9fd2149b..4a20c424 100644 --- a/benchmarks/pywrapfn.jl +++ b/benchmarks/pywrapfn.jl @@ -81,7 +81,7 @@ See check_pyargsptr(nargs::Int) above """ function check_pyargsptr(pf::PyWrapFn{N, RT}) where {N, RT} if unsafe_load(pf.pyargsptr).ob_refcnt > 1 - pydecref_(pf.pyargsptr) + pydecref_unsafe_(pf.pyargsptr) pf.pyargsptr = @pycheckn ccall((@pysym :PyTuple_New), PyPtr, (Int,), nargs) end diff --git a/src/PyCall.jl b/src/PyCall.jl index f1987a29..57dbc0a6 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -76,11 +76,24 @@ mutable struct PyObject o::PyPtr # the actual PyObject* function PyObject(o::PyPtr) po = new(o) - finalizer(pydecref, po) + finalizer(pydecref_safe_, po) return po end end +const PYDECREF_PYOBJECT_LOCK = ReentrantLock() + +function pydecref_safe_(po::PyObject) + # If available, we lock and decref + !islocked(PYDECREF_PYOBJECT_LOCK) && + trylock(() -> (pydecref_unsafe_(po); true), PYDECREF_PYOBJECT_LOCK) && + return nothing + + # Add back to queue to be decref'd later + finalizer(pydecref_safe_, po) + return nothing +end + PyPtr(o::PyObject) = getfield(o, :o) """ @@ -114,13 +127,13 @@ it is equivalent to a `PyNULL()` object. """ ispynull(o::PyObject) = o ≛ PyPtr_NULL -function pydecref_(o::Union{PyPtr,PyObject}) +function pydecref_unsafe_(o::Union{PyPtr,PyObject}) _finalized[] || ccall(@pysym(:Py_DecRef), Cvoid, (PyPtr,), o) return o end function pydecref(o::PyObject) - pydecref_(o) + pydecref_unsafe_(o) setfield!(o, :o, PyPtr_NULL) return o end diff --git a/src/pybuffer.jl b/src/pybuffer.jl index a2cec72d..21ed99cd 100644 --- a/src/pybuffer.jl +++ b/src/pybuffer.jl @@ -32,11 +32,24 @@ mutable struct PyBuffer b = new(Py_buffer(C_NULL, PyPtr_NULL, 0, 0, 0, 0, C_NULL, C_NULL, C_NULL, C_NULL, C_NULL, C_NULL, C_NULL)) - finalizer(pydecref, b) + finalizer(pydecref_safe_, b) return b end end +const PYDECREF_PYBUFFER_LOCK = ReentrantLock() + +function pydecref_safe_(b::PyBuffer) + # If available, we lock and decref + !islocked(PYDECREF_PYBUFFER_LOCK) && + trylock(() -> (pydecref(b); true), PYDECREF_PYBUFFER_LOCK) && + return nothing + + # Add back to queue to be decref'd later + finalizer(pydecref_safe_, b) + return nothing +end + """ `pydecref(o::PyBuffer)` Release the reference to buffer `o` diff --git a/src/pyfncall.jl b/src/pyfncall.jl index 11e81287..a70aad05 100644 --- a/src/pyfncall.jl +++ b/src/pyfncall.jl @@ -28,7 +28,7 @@ function _pycall!(ret::PyObject, o::Union{PyObject,PyPtr}, args, nargs::Int=leng end return __pycall!(ret, pyargsptr, o, kw) #::PyObject finally - pydecref_(pyargsptr) + pydecref_unsafe_(pyargsptr) end end @@ -42,7 +42,7 @@ function __pycall!(ret::PyObject, pyargsptr::PyPtr, o::Union{PyObject,PyPtr}, disable_sigint() do retptr = @pycheckn ccall((@pysym :PyObject_Call), PyPtr, (PyPtr,PyPtr,PyPtr), o, pyargsptr, kw) - pydecref_(ret) + pydecref_unsafe_(ret) setfield!(ret, :o, retptr) end return ret #::PyObject diff --git a/src/pyinit.jl b/src/pyinit.jl index b3371a43..0f3b2987 100644 --- a/src/pyinit.jl +++ b/src/pyinit.jl @@ -110,7 +110,7 @@ end ######################################################################### const _finalized = Ref(false) -# This flag is set via `Py_AtExit` to avoid calling `pydecref_` after +# This flag is set via `Py_AtExit` to avoid calling `pydecref_unsafe_` after # Python is finalized. function _set_finalized()