Fix and rewrite the memory management for capsules.

This commit is contained in:
Siu Kwan Lam 2013-01-22 15:31:13 -06:00
commit f88036274a
7 changed files with 69 additions and 74 deletions

View file

@ -1,4 +1,5 @@
PYMODS = _Debug raw_ostream Type DerivedTypes LLVMContext StringRef AssemblyAnnotationWriter Module PYTHON = python
PYMODS = raw_ostream Type DerivedTypes LLVMContext StringRef AssemblyAnnotationWriter Module
all: _api.so _capsule.so all: _api.so _capsule.so

View file

@ -45,13 +45,15 @@ def mangle(name):
name = name.replace('_', '__').replace(' ', '_') name = name.replace('_', '__').replace(' ', '_')
return name.replace('::', '_').rstrip('*&') return name.replace('::', '_').rstrip('*&')
def pycapsule_new(println, ptr, name, clsname, dtor='capsule_destructor'): def pycapsule_new(println, ptr, name, clsname, dtor=NULL):
# build capsule # build capsule
name_soften = mangle(name) name_soften = mangle(name)
var = new_symbol('pycap_%s' % name_soften) var = new_symbol('pycap_%s' % name_soften)
fmt = 'PyObject* %(var)s = PyCapsule_New(%(ptr)s, "%(name)s", %(dtor)s);' fmt = 'PyObject* %(var)s = PyCapsule_New(%(ptr)s, "%(name)s", %(dtor)s);'
println(fmt % locals()) println(fmt % locals())
println('if (!%(var)s) return NULL;' % locals())
# build context # build context
fmt = 'new CapsuleContext("%(clsname)s")' fmt = 'new CapsuleContext("%(clsname)s")'
context = declare(println, 'CapsuleContext*', fmt % locals()) context = declare(println, 'CapsuleContext*', fmt % locals())

View file

@ -66,34 +66,6 @@ PyObject* getClassName(PyObject* self, PyObject* args) {
} }
} }
static
PyObject* setDestructor(PyObject* self, PyObject* args) {
PyObject* cap;
PyObject* callable;
if (!PyArg_ParseTuple(args, "OO", &cap, &callable)) {
return NULL;
}
PyObject* arglist = Py_BuildValue("(O)", cap);
CapsuleContext* context = getContext(self, arglist);
Py_DECREF(arglist);
if (!context) {
return NULL;
} else {
void* ptr;
if (callable != Py_None) {
if (PyCallable_Check(callable)) {
ptr = callable;
} else {
PyErr_SetString(PyExc_TypeError, "Argument is not callable.");
return NULL;
}
} else {
ptr = NULL;
}
context->destructor = (Destructor_Fn)ptr;
}
Py_RETURN_NONE;
}
static PyMethodDef core_methods[] = { static PyMethodDef core_methods[] = {
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL } #define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
@ -101,7 +73,6 @@ static PyMethodDef core_methods[] = {
declmethod(getPointer), declmethod(getPointer),
declmethod(check), declmethod(check),
declmethod(getClassName), declmethod(getClassName),
declmethod(setDestructor),
{ NULL }, { NULL },
#undef declmethod #undef declmethod
}; };

View file

@ -1,18 +1,44 @@
import _capsule import _capsule
from weakref import WeakValueDictionary from weakref import WeakValueDictionary, ref
import logging
logger = logging.getLogger(__name__)
def set_debug(enabled):
'''
Side-effect: configure logger with it is not configured.
'''
if enabled:
# If no handlers are configured for the root logger,
# build a default handler for debugging.
# Can we do better?
if not logger.root.handlers:
logging.basicConfig()
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.WARNING)
class WeakRef(ref):
__slots__ = 'capsule', 'dtor'
_pyclasses = {} _pyclasses = {}
_addr2obj = WeakValueDictionary() _addr2obj = WeakValueDictionary()
_owners = {}
def _sentry(ptr): def _sentry(ptr):
assert _capsule.check(ptr) assert _capsule.check(ptr)
def classof(cap): def classof(cap):
cls = _capsule.getClassName(cap) cls = _capsule.getClassName(cap)
return _pyclasses[cls] return _pyclasses[cls]
def _capsule_destructor(weak):
cap = weak.capsule
addr = _capsule.getPointer(cap)
cls = _capsule.getClassName(cap)
logger.debug("destroy pointer %s to %s", addr, cls)
weak.dtor(cap)
del _owners[addr]
def wrap(cap): def wrap(cap):
'''Wrap a PyCapsule with the corresponding Wrapper class. '''Wrap a PyCapsule with the corresponding Wrapper class.
@ -29,9 +55,12 @@ def wrap(cap):
cls = classof(cap) cls = classof(cap)
obj = cls(cap) obj = cls(cap)
_addr2obj[addr] = obj # cache object by address _addr2obj[addr] = obj # cache object by address
# set destructor if cls.delete is defined # set ownership if *cls* defines *_delete_*
if hasattr(cls, '_delete_'): if hasattr(cls, '_delete_'):
_capsule.setDestructor(cap, cls._delete_) weak = WeakRef(obj, _capsule_destructor)
_owners[addr] = weak
weak.capsule = cap
weak.dtor = cls._delete_
else: else:
assert classof(obj._ptr) is classof(cap) assert classof(obj._ptr) is classof(cap)
# Unset destructor for capsules that are repeated # Unset destructor for capsules that are repeated

View file

@ -1,48 +1,19 @@
#ifndef LLVMPY_CAPSULE_CONTEXT_H_ #ifndef LLVMPY_CAPSULE_CONTEXT_H_
#define LLVMPY_CAPSULE_CONTEXT_H_ #define LLVMPY_CAPSULE_CONTEXT_H_
#include <iostream> #include <iostream>
#include <ctime> #include <ctime>
typedef PyObject* Destructor_Fn; typedef PyObject* Destructor_Fn;
static bool CapsuleContextDebug = false;
struct CapsuleContext { struct CapsuleContext {
const char* className; const char* className;
Destructor_Fn destructor;
CapsuleContext(const char* cn)
CapsuleContext(const char* cn, Destructor_Fn dtor=NULL) : className(cn)
: className(cn), destructor(dtor) { } { }
}; };
void capsule_destructor(PyObject* capsule){
using std::cerr;
using std::endl;
CapsuleContext* context = (CapsuleContext*)PyCapsule_GetContext(capsule);
if (context->destructor) {
if (CapsuleContextDebug) {
cerr << clock()
<< " == DEBUG =="
<< " destroy pointer: "
<< context->className
<< endl;
}
PyObject_CallMethodObjArgs(context->destructor, capsule, NULL);
} else {
if (CapsuleContextDebug) {
cerr << clock()
<< " == DEBUG =="
<< " keep pointer alive: "
<< context->className
<< endl;
}
}
delete context;
}
void enable_capsule_dtor_debug(bool enabled){
CapsuleContextDebug = enabled;
}
#endif //LLVMPY_CAPSULE_CONTEXT_H_ #endif //LLVMPY_CAPSULE_CONTEXT_H_

View file

@ -1,3 +0,0 @@
from binding import *
enable_capsule_dtor_debug = Function('', Void, Bool.From(bool))

View file

@ -1,20 +1,44 @@
import api import api
#api.enable_capsule_dtor_debug(True) #api.capsule.set_debug(True)
context = api.getGlobalContext() context = api.getGlobalContext()
def test():
print '*' * 80
m = api.Module.new("modname", context)
print m.getModuleIdentifier()
m.setModuleIdentifier('modname2')
print m.getModuleIdentifier()
print 'endianness', m.getEndianness()
assert m.getEndianness() == api.Module.Endianness.AnyEndianness
print 'pointer-size', m.getPointerSize()
assert m.getPointerSize() == api.Module.PointerSize.AnyPointerSize
m.dump()
m = api.Module.new("modname", context) m = api.Module.new("modname", context)
print m.getModuleIdentifier() print m.getModuleIdentifier()
m.setModuleIdentifier('modname2') m.setModuleIdentifier('modname2')
print m.getModuleIdentifier() print m.getModuleIdentifier()
print 'endianness', m.getEndianness()
assert m.getEndianness() == api.Module.Endianness.AnyEndianness
print 'pointer-size', m.getPointerSize()
assert m.getPointerSize() == api.Module.PointerSize.AnyPointerSize
m.dump() m.dump()
os = api.raw_svector_ostream_helper.create() os = api.raw_svector_ostream_helper.create()
m.print_(os, None) m.print_(os, None)
print os.str() print os.str()
int1ty = api.Type.getInt1Ty(context) int1ty = api.Type.getInt1Ty(context)
int1ty.dump() int1ty.dump()
print int1ty.isIntegerTy(1) print int1ty.isIntegerTy(1)
fnty = api.FunctionType.get(int1ty, False)
os2 = api.raw_svector_ostream_helper.create()
fnty.print_(os2)
print os2.str()