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

View file

@ -45,13 +45,15 @@ def mangle(name):
name = name.replace('_', '__').replace(' ', '_')
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
name_soften = mangle(name)
var = new_symbol('pycap_%s' % name_soften)
fmt = 'PyObject* %(var)s = PyCapsule_New(%(ptr)s, "%(name)s", %(dtor)s);'
println(fmt % locals())
println('if (!%(var)s) return NULL;' % locals())
# build context
fmt = 'new CapsuleContext("%(clsname)s")'
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[] = {
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
@ -101,7 +73,6 @@ static PyMethodDef core_methods[] = {
declmethod(getPointer),
declmethod(check),
declmethod(getClassName),
declmethod(setDestructor),
{ NULL },
#undef declmethod
};

View file

@ -1,18 +1,44 @@
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 = {}
_addr2obj = WeakValueDictionary()
_owners = {}
def _sentry(ptr):
assert _capsule.check(ptr)
def classof(cap):
cls = _capsule.getClassName(cap)
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):
'''Wrap a PyCapsule with the corresponding Wrapper class.
@ -29,9 +55,12 @@ def wrap(cap):
cls = classof(cap)
obj = cls(cap)
_addr2obj[addr] = obj # cache object by address
# set destructor if cls.delete is defined
# set ownership if *cls* defines *_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:
assert classof(obj._ptr) is classof(cap)
# Unset destructor for capsules that are repeated

View file

@ -1,48 +1,19 @@
#ifndef LLVMPY_CAPSULE_CONTEXT_H_
#define LLVMPY_CAPSULE_CONTEXT_H_
#include <iostream>
#include <ctime>
typedef PyObject* Destructor_Fn;
static bool CapsuleContextDebug = false;
struct CapsuleContext {
const char* className;
Destructor_Fn destructor;
CapsuleContext(const char* cn, Destructor_Fn dtor=NULL)
: className(cn), destructor(dtor) { }
CapsuleContext(const char* cn)
: className(cn)
{ }
};
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_

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
#api.enable_capsule_dtor_debug(True)
#api.capsule.set_debug(True)
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)
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()
os = api.raw_svector_ostream_helper.create()
m.print_(os, None)
print os.str()
int1ty = api.Type.getInt1Ty(context)
int1ty.dump()
print int1ty.isIntegerTy(1)
fnty = api.FunctionType.get(int1ty, False)
os2 = api.raw_svector_ostream_helper.create()
fnty.print_(os2)
print os2.str()