Introduce finalizer module to manage finalization of C resources.

This commit is contained in:
Siu Kwan Lam 2013-01-03 13:58:08 -06:00
commit bc20661343
7 changed files with 110 additions and 40 deletions

View file

@ -6,6 +6,8 @@ __version__ = '0.9.1'
from weakref import WeakValueDictionary from weakref import WeakValueDictionary
from llvm._utils.finalizer import track as track_resource
from llvm._utils.finalizer import OwnerMixin as _OwnerMixin
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
@ -19,11 +21,26 @@ class LLVMException(Exception):
Exception.__init__(self, msg) Exception.__init__(self, msg)
#===----------------------------------------------------------------------===
# Resource Handle
#===----------------------------------------------------------------------===
class Handle(_OwnerMixin):
_finalizer = NotImplemented
def __init__(self, ptr):
self.ptr = ptr
self._finalizer_track(self.ptr)
@classmethod
def _finalize(cls, ptr):
cls._finalizer(ptr)
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
# Ownables # Ownables
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class Ownable(object): class Ownable(Handle):
"""Objects that can be owned. """Objects that can be owned.
Modules and Module Providers can be owned, i.e., the responsibility of Modules and Module Providers can be owned, i.e., the responsibility of
@ -32,25 +49,21 @@ class Ownable(object):
is NOT intended for public use. is NOT intended for public use.
""" """
def __init__(self, ptr, del_fn): def __init__(self, ptr):
self.ptr = ptr Handle.__init__(self, ptr)
self.owner = None self.owner = None
self.del_fn = del_fn
def _own(self, owner): def _own(self, owner):
if self.owner: if self.owner:
raise LLVMException("object already owned") raise LLVMException("object already owned")
self.owner = owner self.owner = owner
self._finalizer_untrack(self.ptr)
def _disown(self): def _disown(self):
if not self.owner: if not self.owner:
raise LLVMException("not owned") raise LLVMException("not owned")
self.owner = None self.owner = None
self._finalizer_track(self.ptr)
def __del__(self):
if not self.owner:
self.del_fn(self.ptr)
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
# Dummy owner, will not delete ownee. Be careful. # Dummy owner, will not delete ownee. Be careful.

0
llvm/_utils/__init__.py Normal file
View file

58
llvm/_utils/finalizer.py Normal file
View file

@ -0,0 +1,58 @@
'''
Modified C-level finalizer by Benjamin Peterson <benjamin@python.org>
Available at http://code.activestate.com/recipes/577242-calling-c-level-finalizers-without-__del__/
'''
import sys
import traceback
import weakref
class OwnerRef(weakref.ref):
"""A simple weakref.ref subclass, so attributes can be added."""
pass
def _run_finalizer(ref):
"""Internal weakref callback to run finalizers"""
del _finalize_refs[ref.owner]
for item, finalizer in ref.items:
try:
finalizer(item)
except Exception:
print>>sys.stderr, "Exception running {}:".format(finalizer)
traceback.print_exc()
_finalize_refs = {}
def track(owner, item, finalizer):
"""Register an object for finalization.
``owner`` is the the object which is responsible for ``item``.
``finalizer`` will be called with ``item`` as its only argument when
``owner`` is destroyed by the garbage collector.
"""
if id(owner) in _finalize_refs:
ref = _finalize_refs[id(owner)]
else:
ref = OwnerRef(owner, _run_finalizer)
ref.owner = id(owner)
ref.items = []
ref.items.append((item, finalizer))
_finalize_refs[id(owner)] = ref
def untrack(owner, item):
ref = _finalize_refs[id(owner)]
# search and remove the item from the ref
for i, (item_i, _) in enumerate(ref.items):
if item_i is item:
del ref.items[i]
# remove the ref if it is empty
if not ref.items:
del _finalize_refs[id(owner)]
class OwnerMixin(object):
def _finalizer_track(self, item):
if not hasattr(self, '_finalize'):
raise AttributeError("%s must define a _finalize method" % self)
track(self, item, type(self)._finalize)
def _finalizer_untrack(self, item):
untrack(self, item)

View file

@ -396,7 +396,9 @@ class Module(llvm.Ownable, llvm.Cacheable):
Use the static method `Module.new' instead. Use the static method `Module.new' instead.
""" """
llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeModule) llvm.Ownable.__init__(self, ptr)
_finalizer = _core.LLVMDisposeModule
def __str__(self): def __str__(self):
"""Text representation of a module. """Text representation of a module.
@ -1798,7 +1800,7 @@ def _make_value(ptr):
# Builder # Builder
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class Builder(object): class Builder(llvm.Handle):
@staticmethod @staticmethod
def new(basic_block): def new(basic_block):
@ -1808,10 +1810,9 @@ class Builder(object):
return b return b
def __init__(self, ptr): def __init__(self, ptr):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
def __del__(self): _finalizer = _core.LLVMDisposeBuilder
_core.LLVMDisposeBuilder(self.ptr)
def position_at_beginning(self, bblk): def position_at_beginning(self, bblk):
"""Position the builder at the beginning of the given block. """Position the builder at the beginning of the given block.
@ -2300,7 +2301,7 @@ class Builder(object):
# Memory buffer # Memory buffer
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class MemoryBuffer(object): class MemoryBuffer(llvm.Handle):
@staticmethod @staticmethod
def from_file(fname): def from_file(fname):
@ -2321,10 +2322,9 @@ class MemoryBuffer(object):
return (obj, "") return (obj, "")
def __init__(self, ptr): def __init__(self, ptr):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
def __del__(self): _finalizer = _core.LLVMDisposeMemoryBuffer
_core.LLVMDisposeMemoryBuffer(self.ptr)
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===

View file

@ -56,7 +56,7 @@ BO_LITTLE_ENDIAN = 1
# Generic value # Generic value
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class GenericValue(object): class GenericValue(llvm.Handle):
@staticmethod @staticmethod
def int(ty, intval): def int(ty, intval):
@ -99,10 +99,9 @@ class GenericValue(object):
return GenericValue(ptr) return GenericValue(ptr)
def __init__(self, ptr): def __init__(self, ptr):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
def __del__(self): _finalizer = _core.LLVMDisposeGenericValue
_core.LLVMDisposeGenericValue(self.ptr)
def as_int(self): def as_int(self):
return _core.LLVMGenericValueToInt(self.ptr, 0) return _core.LLVMGenericValueToInt(self.ptr, 0)
@ -128,7 +127,7 @@ def _unpack_generic_values(objlist):
# Engine builder # Engine builder
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class EngineBuilder(object): class EngineBuilder(llvm.Handle):
@staticmethod @staticmethod
def new(module): def new(module):
core.check_is_module(module) core.check_is_module(module)
@ -137,11 +136,10 @@ class EngineBuilder(object):
return EngineBuilder(obj, module) return EngineBuilder(obj, module)
def __init__(self, ptr, module): def __init__(self, ptr, module):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
self._module = module self._module = module
def __del__(self): _finalizer = _core.LLVMDisposeEngineBuilder
_core.LLVMDisposeEngineBuilder(self.ptr)
def force_jit(self): def force_jit(self):
_core.LLVMEngineBuilderForceJIT(self.ptr) _core.LLVMEngineBuilderForceJIT(self.ptr)
@ -193,7 +191,7 @@ class EngineBuilder(object):
# Execution engine # Execution engine
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class ExecutionEngine(object): class ExecutionEngine(llvm.Handle):
@staticmethod @staticmethod
def new(module, force_interpreter=False): def new(module, force_interpreter=False):
@ -205,11 +203,10 @@ class ExecutionEngine(object):
return ExecutionEngine(ret, module) return ExecutionEngine(ret, module)
def __init__(self, ptr, module): def __init__(self, ptr, module):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
module._own(self) module._own(self)
def __del__(self): _finalizer = _core.LLVMDisposeExecutionEngine
_core.LLVMDisposeExecutionEngine(self.ptr)
def run_function(self, fn, args): def run_function(self, fn, args):
core.check_is_function(fn) core.check_is_function(fn)
@ -299,7 +296,9 @@ class TargetMachine(llvm.Ownable):
return TargetMachine(ptr) return TargetMachine(ptr)
def __init__(self, ptr): def __init__(self, ptr):
llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeTargetMachine) llvm.Ownable.__init__(self, ptr)
_finalizer = _core.LLVMDisposeTargetMachine
def emit_assembly(self, module): def emit_assembly(self, module):
'''returns byte string of the module as assembly code of the target machine '''returns byte string of the module as assembly code of the target machine

View file

@ -45,16 +45,15 @@ import warnings
# Pass manager builder # Pass manager builder
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class PassManagerBuilder(object): class PassManagerBuilder(llvm.Handle):
@staticmethod @staticmethod
def new(): def new():
return PassManagerBuilder(_core.LLVMPassManagerBuilderCreate()) return PassManagerBuilder(_core.LLVMPassManagerBuilderCreate())
def __init__(self, ptr): def __init__(self, ptr):
self.ptr = ptr llvm.Handle.__init__(self, ptr)
def __del__(self): _finalizer = _core.LLVMPassManagerBuilderDispose
_core.LLVMPassManagerBuilderDispose(self.ptr)
def populate(self, pm): def populate(self, pm):
if isinstance(pm, FunctionPassManager): if isinstance(pm, FunctionPassManager):
@ -142,7 +141,7 @@ class PassManagerBuilder(object):
# Pass manager # Pass manager
#===----------------------------------------------------------------------=== #===----------------------------------------------------------------------===
class PassManager(object): class PassManager(llvm.Handle):
@staticmethod @staticmethod
def new(): def new():
@ -151,8 +150,7 @@ class PassManager(object):
def __init__(self, ptr): def __init__(self, ptr):
self.ptr = ptr self.ptr = ptr
def __del__(self): _finalizer = _core.LLVMDisposePassManager
_core.LLVMDisposePassManager(self.ptr)
def add(self, pass_obj): def add(self, pass_obj):
'''Add a pass to the pass manager. '''Add a pass to the pass manager.
@ -208,9 +206,11 @@ class Pass(llvm.Ownable):
'''Pass Inferface '''Pass Inferface
''' '''
def __init__(self, ptr): def __init__(self, ptr):
llvm.Ownable.__init__(self, ptr, _core.LLVMDisposePass) llvm.Ownable.__init__(self, ptr)
self.__name = '' self.__name = ''
_finalizer = _core.LLVMDisposePass
@staticmethod @staticmethod
def new(name): def new(name):
'''Create a new pass by name. '''Create a new pass by name.

View file

@ -131,7 +131,7 @@ setup(
maintainer = 'Continuum Analytics, Inc.', maintainer = 'Continuum Analytics, Inc.',
maintainer_email = 'llvmpy@continuum.io', maintainer_email = 'llvmpy@continuum.io',
url = 'http://www.llvmpy.org/', url = 'http://www.llvmpy.org/',
packages = ['llvm', 'llvm_cbuilder', 'llpython'], packages = ['llvm', 'llvm_cbuilder', 'llpython', 'llvm._utils'],
py_modules = ['llvm.core'], py_modules = ['llvm.core'],
license = "BSD", license = "BSD",
classifiers = [ classifiers = [