Fix memleak and simplify dynamic client api

This commit is contained in:
Jason Paryani
2013-10-17 19:15:40 -07:00
parent a5d0abb49f
commit 7f7b28f328
5 changed files with 91 additions and 117 deletions

View File

@@ -12,8 +12,14 @@ class PythonInterfaceDynamicImpl final: public capnp::DynamicCapability::Server
public:
PyObject * py_server;
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema, PyObject * py_server)
: capnp::DynamicCapability::Server(schema), py_server(py_server) {}
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema, PyObject * _py_server)
: capnp::DynamicCapability::Server(schema), py_server(_py_server) {
Py_INCREF(_py_server);
}
~PythonInterfaceDynamicImpl() {
Py_DECREF(py_server);
}
kj::Promise<void> call(capnp::InterfaceSchema::Method method,
capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> context) {

View File

@@ -9,7 +9,7 @@
cimport cython
cimport capnp_cpp as capnp
cimport schema_cpp
from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, ObjectPointer as C_DynamicObject, WordArrayPtr, DynamicCapability as C_DynamicCapability, new_client, Request, RemotePromise, convert_to_pypromise, SimpleEventLoop, PyPromise, CallContext
from capnp_cpp cimport Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema, DynamicStruct as C_DynamicStruct, DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, fixMaybe, getEnumString, SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr, String, StringTree, DynamicOrphan as C_DynamicOrphan, ObjectPointer as C_DynamicObject, WordArrayPtr, DynamicCapability as C_DynamicCapability, new_client, Request, Response, RemotePromise, convert_to_pypromise, SimpleEventLoop, PyPromise, CallContext
from schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode
from cython.operator cimport dereference as deref
@@ -34,6 +34,12 @@ ctypedef double Float64
from libc.stdlib cimport malloc, free
from libcpp cimport bool as cbool
from types import ModuleType as _ModuleType
import os as _os
import sys as _sys
import imp as _imp
from functools import partial as _partial
# By making it public, we'll be able to call it from capabilityHelper.h
cdef public object wrap_dynamic_struct_reader(C_DynamicStruct.Reader & reader):
return _DynamicStructReader()._init(reader, None)
@@ -95,6 +101,7 @@ cdef extern from "capnp/list.h" namespace " ::capnp":
cdef extern from "<utility>" namespace "std":
C_DynamicOrphan moveOrphan"std::move"(C_DynamicOrphan)
Request moveRequest"std::move"(Request)
Response moveResponse"std::move"(Response)
PyPromise movePromise"std::move"(PyPromise)
RemotePromise moveRemotePromise"std::move"(RemotePromise)
CallContext moveCallContext"std::move"(CallContext)
@@ -969,7 +976,7 @@ cdef class EventLoop:
if promise.is_consumed:
raise RuntimeError('Promise was already used in a consuming operation. You can no longer use this Promise object')
ret = _DynamicStructReader()._init(self.thisptr.wait_remote(moveRemotePromise(deref(promise.thisptr))), promise._parent)
ret = _Response()._init_child(self.thisptr.wait_remote(moveRemotePromise(deref(promise.thisptr))), promise._parent)
promise.is_consumed = True
return ret
@@ -982,108 +989,24 @@ cdef class EventLoop:
# Py_INCREF(error_func)
# return Promise()._init(capnp.there(self.thisptr, deref(promise.thisptr), <PyObject *>func, <PyObject *>error_func))
cdef class _Request:
cdef Request * thisptr
cdef public object _parent
cdef class _Request(_DynamicStructBuilder):
cdef Request * thisptr_child
cdef _init(self, Request other, parent):
self.thisptr = new Request(moveRequest(other))
self._parent = parent
cdef _init_child(self, Request other, parent):
self.thisptr_child = new Request(moveRequest(other))
self._init(<C_DynamicStruct.Builder>deref(self.thisptr_child), parent)
return self
cpdef send(self):
return _RemotePromise()._init(self.thisptr.send(), self._parent)
cdef _get(self, field):
cdef C_DynamicValue.Builder value = self.thisptr.get(field)
return _RemotePromise()._init(self.thisptr_child.send(), self._parent)
return to_python_builder(value, self._parent)
def __getattr__(self, field):
return self._get(field)
cdef class _Response(_DynamicStructReader):
cdef Response * thisptr_child
def __setattr__(self, field, value):
_setDynamicFieldPtr(self.thisptr, field, value, self._parent)
def _has(self, field):
return self.thisptr.has(field)
cpdef init(self, field, size=None):
"""Method for initializing fields that are of type union/struct/list
Typically, you don't have to worry about initializing structs/unions, so this method is mainly for lists.
:type field: str
:param field: The field name to initialize
:type size: int
:param size: The size of the list to initiialize. This should be None for struct/union initialization.
:rtype: :class:`_DynamicStructBuilder` or :class:`_DynamicListBuilder`
:Raises: :exc:`exceptions.ValueError` if the field isn't in this struct
"""
if size is None:
return to_python_builder(self.thisptr.init(field), self._parent)
else:
return to_python_builder(self.thisptr.init(field, size), self._parent)
cpdef init_resizable_list(self, field):
"""Method for initializing fields that are of type list (of structs)
This version of init returns a :class:`_DynamicResizableListBuilder` that allows you to add members one at a time (ie. if you don't know the size for sure). This is only meant for lists of Cap'n Proto objects, since for primitive types you can just define a normal python list and fill it yourself.
.. warning:: You need to call :meth:`_DynamicResizableListBuilder.finish` on the list object before serializing the Cap'n Proto message. Failure to do so will cause your objects not to be written out as well as leaking orphan structs into your message.
:type field: str
:param field: The field name to initialize
:rtype: :class:`_DynamicResizableListBuilder`
:Raises: :exc:`exceptions.ValueError` if the field isn't in this struct
"""
return _DynamicResizableListBuilder(self, field, _StructSchema()._init((<C_DynamicValue.Builder>self.thisptr.get(field)).asList().getStructElementType()))
cpdef which(self):
"""Returns the enum corresponding to the union in this struct
Enums are just strings in the python Cap'n Proto API, so this function will either return a string equal to the field name of the active field in the union, or throw a ValueError if this isn't a union, or a struct with an unnamed union::
person = addressbook.Person.new_message()
person.which()
# ValueError: member was null
a.employment.employer = 'foo'
print employment.which()
# 'employer'
:rtype: str
:return: A string/enum corresponding to what field is set in the union
:Raises: :exc:`exceptions.ValueError` if this struct doesn't contain a union
"""
cdef object which = getEnumString(deref(self.thisptr))
if len(which) == 0:
raise ValueError("Attempted to call which on a non-union type")
return which
property schema:
"""A property that returns the _StructSchema object matching this writer"""
def __get__(self):
return _StructSchema()._init(self.thisptr.getSchema())
def __dir__(self):
return list(self.schema.fieldnames)
def __str__(self):
return printRequest(deref(self.thisptr)).flatten().cStr()
def __repr__(self):
return '<%s builder %s>' % (self.schema.node.displayName, strRequest(deref(self.thisptr)).cStr())
def to_dict(self):
return _to_dict(self)
cdef _init_child(self, Response other, parent):
self.thisptr_child = new Response(moveResponse(other))
self._init(<C_DynamicStruct.Reader>deref(self.thisptr_child), parent)
return self
cdef class _DynamicCapabilityClient:
cdef C_DynamicCapability.Client thisptr
@@ -1101,7 +1024,7 @@ cdef class _DynamicCapabilityClient:
self.thisptr = new_client(s.thisptr, <PyObject *>server, loop.thisptr)
self._server = server
cpdef _new_request_helper(self, name, firstSegmentWordSize, kwargs) except +ValueError:
cpdef _send_helper(self, name, firstSegmentWordSize, kwargs) except +ValueError:
cdef Request * request = new Request(self.thisptr.newRequest(name, firstSegmentWordSize))
for key, val in kwargs.items():
@@ -1109,11 +1032,20 @@ cdef class _DynamicCapabilityClient:
return _RemotePromise()._init(request.send(), self)
cpdef request(self, name, firstSegmentWordSize=0) except +ValueError:
return _Request()._init(self.thisptr.newRequest(name, firstSegmentWordSize), self)
cpdef _request_helper(self, name, firstSegmentWordSize=0) except +ValueError:
return _Request()._init_child(self.thisptr.newRequest(name, firstSegmentWordSize), self)
def send(self, name, firstSegmentWordSize=0, **kwargs):
return self._new_request_helper(name, firstSegmentWordSize, kwargs)
def _request(self, name, firstSegmentWordSize=0):
return self._request_helper(name, firstSegmentWordSize)
def _send(self, name, *args, firstSegmentWordSize=0, **kwargs):
return self._send_helper(name, firstSegmentWordSize, kwargs)
def __getattr__(self, name):
if name.endswith('_request'):
short_name = name[:-8]
return _partial(self._request, short_name)
return _partial(self._send, name)
cdef class _Schema:
cdef C_Schema thisptr
@@ -1630,11 +1562,6 @@ def _write_packed_message_to_fd(int fd, _MessageBuilder message):
"""
schema_cpp.writePackedMessageToFd(fd, deref(message.thisptr))
from types import ModuleType as _ModuleType
import os as _os
import sys as _sys
import imp as _imp
_global_schema_parser = None
def load(file_name, display_name=None, imports=[]):

View File

@@ -150,7 +150,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef extern from "capnp/capability.h" namespace " ::capnp":
cdef cppclass Response" ::capnp::Response< ::capnp::DynamicStruct>"(DynamicStruct.Reader):
pass
Response(Response)
cdef cppclass RemotePromise" ::capnp::RemotePromise< ::capnp::DynamicStruct>"(Promise[Response]):
RemotePromise(RemotePromise)
@@ -291,7 +291,7 @@ cdef extern from "kj/async.h" namespace " ::kj":
EventLoop()
# Promise[void] yield_end'yield'()
object wait(PyPromise) except+
DynamicStruct.Reader wait_remote'wait'(RemotePromise) except+
Response wait_remote'wait'(RemotePromise)
object there(PyPromise) except+
PyPromise evalLater(PyObject * func)
PyPromise there(PyPromise, PyObject * func)