Skip to content

Commit

Permalink
[Python] Improved fix for cleanup of Solution change callbacks
Browse files Browse the repository at this point in the history
Fixes a case not handled by 2158ff9 for "weak" wrappers where errors
could occur if the destructor for the weak wrapper was called after the
destructor for the owning Solution object.

Fixes #1489
  • Loading branch information
speth authored and ischoegl committed Jun 11, 2023
1 parent a40579e commit bb74ba5
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 5 deletions.
2 changes: 1 addition & 1 deletion interfaces/cython/cantera/ctcxx.pxd
Expand Up @@ -11,7 +11,7 @@ from libcpp.cast cimport dynamic_cast
from libcpp.pair cimport pair
from libcpp cimport bool as cbool
from libcpp.functional cimport function
from libcpp.memory cimport shared_ptr, dynamic_pointer_cast
from libcpp.memory cimport shared_ptr, weak_ptr, dynamic_pointer_cast
from cpython cimport bool as pybool
from cpython.ref cimport PyObject
from cython.operator cimport dereference as deref, preincrement as inc
Expand Down
1 change: 1 addition & 0 deletions interfaces/cython/cantera/solutionbase.pxd
Expand Up @@ -107,6 +107,7 @@ cdef object _wrap_Solution(shared_ptr[CxxSolution] cxx_soln)

cdef class _SolutionBase:
cdef shared_ptr[CxxSolution] _base
cdef weak_ptr[CxxSolution] weak_base
cdef CxxSolution* base
cdef CxxThermoPhase* thermo
cdef CxxKinetics* kinetics
Expand Down
14 changes: 10 additions & 4 deletions interfaces/cython/cantera/solutionbase.pyx
Expand Up @@ -113,8 +113,9 @@ cdef class _SolutionBase:
self.name = name

def __del__(self):
if self.base:
self.base.removeChangedCallback(<PyObject*>self)
cdef shared_ptr[CxxSolution] shared = self.weak_base.lock()
if shared:
shared.get().removeChangedCallback(<PyObject*>self)

property name:
"""
Expand Down Expand Up @@ -433,13 +434,18 @@ cdef class _SolutionBase:
cdef _assign_Solution(_SolutionBase soln, shared_ptr[CxxSolution] cxx_soln,
pybool reset_adjacent, pybool weak=False):
if not weak:
# When the main application isn't Python, we should only hold a weak reference
# here, since the C++ Solution object owns this Python Solution.
# _SolutionBase owns the C++ Solution object by holding the shared_ptr instance
if soln._base.get() != NULL:
soln._base.get().removeChangedCallback(<PyObject*>(soln))
soln._base = cxx_soln
# Make a raw pointer available for most use cases, where existence of the C++
# Solution object is assured.
soln.base = cxx_soln.get()

# Hold a weak_ptr for use in the _SolutionBase destructor, where the C++
# object may have already been destroyed depending on ownership.
soln.weak_base = weak_ptr[CxxSolution](cxx_soln)

def assign_pointers():
soln.thermo = soln.base.thermo().get()
soln.kinetics = soln.base.kinetics().get()
Expand Down

0 comments on commit bb74ba5

Please sign in to comment.