Files
pycapnp/capnp/lib/capnp.pxd
Lasse Blaauwbroek d32854eb00 Integrate the KJ event loop into Python's asyncio event loop (#310)
* Integrate the KJ event loop into Python's asyncio event loop

Fix #256

This PR attempts to remove the slow and expensive polling behavior for asyncio
in favor of proper linking of the KJ event loop to the asyncio event loop.

* Don't memcopy buffer

* Improve promise cancellation and prepare for timer implementation

* Add attribution for asyncProvider.cpp

* Implement timeout

* Cleanup

* First round of simplifications

* Add more a_wait functions and a shutdown function

* Fix edge-cases with loop shutdown

* Clean up calculator examples

* Cleanup

* Cleanup

* Reformat

* Fix warnings

* Reformat again

* Compatibility with macos

* Inline the asyncio loop in some places where this is feasible

* Add todo

* Fix

* Remove synchronous wait

* Wrap fd listening callbacks in a class

* Remove poll_forever

* Remove the thread-local/thread-global optimization

This will not matter much soon anyway, and simplifies things

* Share promise code by using fused types

* Improve refcounting of python objects in promises

We replace many instances of PyObject* by Own<PyRefCounter> for more automatic
reference management.

* Code wrapPyFunc in a similar way to wrapPyFuncNoArg

* Refactor capabilityHelper, fix several memory bugs for promises and add __await__

* Improve promise ownership, reduce memory leaks

Promise wrappers now hold a Own<Promise<Own<PyRefCounter>>> object. This might
seem like excessive nesting of objects (which to some degree it is, but with
good reason):
- The outer Own is needed because Cython cannot allocate objects without a
  nullary constructor on the stack (Promise doesn't have a nullary constructor).
  Additionally, I believe it would be difficult or impossible to detect when a
  promise is cancelled/moved if we use a bare Promise.
- Every promise returns a Owned PyRefCounter. PyRefCounter makes sure that a
  reference to the returned object keeps existing until the promise is fulfilled
  or cancelled. Previously, this was attempted using attach, which is redundant
  and makes reasoning about PyINCREF and PyDECREF very difficult.
- Because a promise holds a Own<Promise<...>>, when we perform any kind of
  action on that promise (a_wait, then, ...), we have to explicitly move() the
  ownership around. This will leave the original promise with a NULL-pointer,
  which we can easily detect as a cancelled promise.

Promises now only hold references to their 'parents' when strictly needed. This
should reduce memory pressure.

* Simplify and test the promise joining functionality

* Attach forgotten parent

* Catch exceptions in add_reader and friends

* Further cleanup of memory leaks

* Get rid of a_wait() in examples

* Cancel all fd read operations when the python asyncio loop is closed

* Formatting

* Remove support for capnp < 7000

* Bring asyncProvider.cpp more in line with upstream async-io-unix.c++

It was originally copied from the nodejs implementation, which in turn copied
from async-io-unix.c++. But that copy is pretty old.

* Fix a bug that caused file descriptors to never be closed

* Implement AsyncIoStream based on Python transports and protocols

* Get rid of asyncProvider

All asyncio now goes through _AsyncIoStream

* Formatting

* Add __dict__ to  PyAsyncIoStreamProtocol for python 3.7

* Reintroduce strange ipv4/ipv6 selection code to make ci happy

* Extra pause_reading()

* Work around more python bugs

* Be careful to only close transport when this is still possible

* Move pause_reading() workaround
2023-06-06 11:08:15 -07:00

168 lines
6.3 KiB
Cython

# cython: language_level = 2
from capnp.includes cimport capnp_cpp as capnp
from capnp.includes cimport schema_cpp
from capnp.includes.capnp_cpp cimport (
Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema,
EnumSchema as C_EnumSchema, ListSchema as C_ListSchema, DynamicStruct as C_DynamicStruct,
DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList,
SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr,
String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject,
DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, Promise,
CallContext, RpcSystem, makeRpcServerBootstrap, makeRpcClient, Capability as C_Capability,
TwoPartyVatNetwork as C_TwoPartyVatNetwork, Side, AsyncIoStream, Own, makeTwoPartyVatNetwork,
PromiseFulfillerPair as C_PromiseFulfillerPair, copyPromiseFulfillerPair, newPromiseAndFulfiller,
PyArray, DynamicStruct_Builder, TwoWayPipe, PyRefCounter, PyAsyncIoStream
)
from capnp.includes.schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode
from capnp.includes.types cimport *
from capnp.helpers.non_circular cimport reraise_kj_exception
from capnp.helpers cimport helpers
cdef class _StructSchemaField:
cdef C_StructSchema.Field thisptr
cdef object _parent
cdef _init(self, C_StructSchema.Field other, parent=?)
cdef class _StringArrayPtr:
cdef StringPtr * thisptr
cdef object parent
cdef size_t size
cdef ArrayPtr[StringPtr] asArrayPtr(self) except +reraise_kj_exception
cdef class SchemaParser:
cdef C_SchemaParser * thisptr
cdef public dict modules_by_id
cdef list _all_imports
cdef _StringArrayPtr _last_import_array
cpdef _parse_disk_file(self, displayName, diskPath, imports) except +reraise_kj_exception
cdef class _DynamicOrphan:
cdef C_DynamicOrphan thisptr
cdef public object _parent
cdef _init(self, C_DynamicOrphan other, object parent)
cdef C_DynamicOrphan move(self)
cpdef get(self)
cdef class _DynamicStructReader:
cdef C_DynamicStruct.Reader thisptr
cdef public object _parent
cdef public bint is_root
cdef object _obj_to_pin
cdef object _schema
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?, bint tryRegistry=?)
cpdef _get(self, field)
cpdef _has(self, field)
cpdef _DynamicEnumField _which(self)
cpdef _which_str(self)
cpdef _get_by_field(self, _StructSchemaField field)
cpdef _has_by_field(self, _StructSchemaField field)
cpdef as_builder(self, num_first_segment_words=?)
cdef class _DynamicStructBuilder:
cdef DynamicStruct_Builder thisptr
cdef public object _parent
cdef public bint is_root
cdef public bint _is_written
cdef object _schema
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?, bint tryRegistry=?)
cdef _check_write(self)
cpdef to_bytes(_DynamicStructBuilder self) except +reraise_kj_exception
cpdef to_segments(_DynamicStructBuilder self) except +reraise_kj_exception
cpdef _to_bytes_packed_helper(_DynamicStructBuilder self, word_count) except +reraise_kj_exception
cpdef to_bytes_packed(_DynamicStructBuilder self) except +reraise_kj_exception
cpdef _get(self, field)
cpdef _set(self, field, value)
cpdef _has(self, field)
cpdef init(self, field, size=?)
cpdef _get_by_field(self, _StructSchemaField field)
cpdef _set_by_field(self, _StructSchemaField field, value)
cpdef _has_by_field(self, _StructSchemaField field)
cpdef _init_by_field(self, _StructSchemaField field, size=?)
cpdef init_resizable_list(self, field)
cpdef _DynamicEnumField _which(self)
cpdef _which_str(self)
cpdef adopt(self, field, _DynamicOrphan orphan)
cpdef disown(self, field)
cpdef as_reader(self)
cpdef copy(self, num_first_segment_words=?)
cdef class _DynamicEnumField:
cdef object thisptr
cdef _init(self, proto)
cpdef _str(self)
cdef class _Schema:
cdef C_Schema thisptr
cdef _init(self, C_Schema other)
cpdef as_const_value(self)
cpdef as_struct(self)
cpdef as_interface(self)
cpdef as_enum(self)
cpdef get_proto(self)
cdef class _InterfaceSchema:
cdef C_InterfaceSchema thisptr
cdef object __method_names, __method_names_inherited, __methods, __methods_inherited
cdef _init(self, C_InterfaceSchema other)
cdef class _DynamicEnum:
cdef capnp.DynamicEnum thisptr
cdef public object _parent
cdef _init(self, capnp.DynamicEnum other, object parent)
cpdef _as_str(self) except +reraise_kj_exception
cdef class _DynamicListBuilder:
cdef C_DynamicList.Builder thisptr
cdef public object _parent
cdef _init(self, C_DynamicList.Builder other, object parent)
cpdef _get(self, int64_t index)
cpdef _set(self, index, value)
cpdef adopt(self, index, _DynamicOrphan orphan)
cpdef disown(self, index)
cpdef init(self, index, size)
cdef class _MessageBuilder:
cdef schema_cpp.MessageBuilder * thisptr
cpdef init_root(self, schema)
cpdef get_root(self, schema) except +reraise_kj_exception
cpdef get_root_as_any(self) except +reraise_kj_exception
cpdef set_root(self, value) except +reraise_kj_exception
cpdef get_segments_for_output(self) except +reraise_kj_exception
cpdef new_orphan(self, schema) except +reraise_kj_exception
cdef to_python_reader(C_DynamicValue.Reader self, object parent)
cdef to_python_builder(C_DynamicValue.Builder self, object parent)
cdef _to_dict(msg, bint verbose, bint ordered)
cdef _from_list(_DynamicListBuilder msg, list d)
cdef _from_tuple(_DynamicListBuilder msg, tuple d)
cdef _setDynamicFieldWithField(DynamicStruct_Builder thisptr, _StructSchemaField field, value, parent)
cdef _setDynamicFieldStatic(DynamicStruct_Builder thisptr, field, value, parent)
cdef api object wrap_dynamic_struct_reader(Response & r) with gil
cdef api Promise[void] * call_server_method(
PyObject * _server, char * _method_name, CallContext & _context) except * with gil
cdef api convert_array_pyobject(PyArray & arr) with gil
cdef api object wrap_kj_exception(capnp.Exception & exception) with gil
cdef api object wrap_kj_exception_for_reraise(capnp.Exception & exception) with gil
cdef api object get_exception_info(object exc_type, object exc_obj, object exc_tb) with gil