Resource Cleanup of Factory-Created DSP Instances¶
The Problem¶
interpreter_dsp objects are allocated via interpreter_dsp_factory::createDSPInstance():
/* Create a new DSP instance, to be deleted with C++ 'delete' */
interpreter_dsp* interpreter_dsp_factory::createDSPInstance();
Factories are created using functions like createInterpreterDSPFactoryFromString(..).
The library keeps an internal cache of all allocated factories so that the
compilation of the same DSP code (same source and same normalized compilation
options) returns the same reference-counted factory pointer. You must explicitly
call deleteDSPFactory to decrement the reference counter when the factory is no
longer needed.
An interpreter_dsp instance depends on the factory that created it. If the
factory is deleted (or garbage collected) before a DSP instance is deallocated,
the result is a segfault.
Note: interpreter_dsp_factory does track its interpreter_dsp pointers and
cleans them up when deleteInterpreterDSPFactory is called, but the solution
below provides explicit Cython-level lifecycle management as well.
Solution¶
InterpreterDspFactory keeps track of created InterpreterDsp instances in a
set and ensures they are cleaned up before the factory is deallocated:
cdef class InterpreterDspFactory:
"""Interpreter DSP factory class."""
cdef fi.interpreter_dsp_factory* ptr
cdef bint ptr_owner
cdef set instances
def __cinit__(self):
self.ptr = NULL
self.ptr_owner = False
self.instances = set()
def __dealloc__(self):
if self.ptr and self.ptr_owner:
instances = self.instances.copy()
while instances:
i = instances.pop()
i.delete()
fi.deleteInterpreterDSPFactory(self.ptr)
self.ptr = NULL
def create_dsp_instance(self) -> InterpreterDsp:
"""Create a new DSP instance, to be deleted with C++ 'delete'"""
cdef fi.interpreter_dsp* dsp = self.ptr.createDSPInstance()
instance = InterpreterDsp.from_ptr(dsp)
self.instances.add(instance)
return instance
cdef class InterpreterDsp:
"""DSP instance class with methods."""
cdef fi.interpreter_dsp* ptr
cdef bint ptr_owner
def __cinit__(self):
self.ptr = NULL
self.ptr_owner = False
def __dealloc__(self):
if self.ptr and self.ptr_owner:
del self.ptr
self.ptr = NULL
def delete(self):
del self.ptr
@staticmethod
cdef InterpreterDsp from_ptr(fi.interpreter_dsp* ptr, bint owner=False):
"""Wrap the dsp instance and manage its lifetime."""
cdef InterpreterDsp dsp = InterpreterDsp.__new__(InterpreterDsp)
dsp.ptr_owner = owner
dsp.ptr = ptr
return dsp