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
|
all: _api.so _capsule.so
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from binding import *
|
|
||||||
|
|
||||||
enable_capsule_dtor_debug = Function('', Void, Bool.From(bool))
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue