Fix and rewrite the memory management for capsules.
This commit is contained in:
parent
089c825b0c
commit
f88036274a
7 changed files with 69 additions and 74 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
from binding import *
|
||||
|
||||
enable_capsule_dtor_debug = Function('', Void, Bool.From(bool))
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue