TwoWayPipe and basic asyncio support
Note: I've tried not to break any behaviour of the previously working APIs
Python API Changes / Additions
- capnp/lib/capnp.pyx
* class _RemotePromise
+ [Added] cpdef _wait(self)
= Exception raising code that used to be inside of wait(self)
+ [Modified] def wait(self)
= Same functionality as before
+ [Added] async def a_wait(self)
= Cannot use await as that's a reserved keyword
= Uses pollRemote and asyncio.sleep(0) to make call asynchronous
* class _TwoPartyVatNetwork
+ [Added] cdef _init_pipe(self, _TwoWayPipe pipe, Side side,
schema_cpp.ReaderOptions opts)
= Instanciates a TwoPartyVatNetwork using a TwoWayPipe (instead of
using a file handle or connection as before)
* class TwoPartyClient
+ [Modified] def __init__(self, socket=None, restorer=None,
traversal_limit_in_words=None, nesting_limit=None)
= Changes the socket parameter to be optional
= If socket is not specified, default to using a TwoWayPipe
+ [Added] async def read(self, bufsize)
= awaitable function that blocks until data has been read
= bufsize defines the maximum amount of data to be read back
(e.g. 4096 bytes)
= Reads data from TwoWayPipe
+ [Added] def write(self, data)
= Write data to TwoWayPipe
= Not awaitable as the write interface of the TwoWayPipe doesn't
have poll functionality
* class TwoPartyServer
+ [Modified] def __init__(self, socket=None, restorer=None,
server_socket=None, bootstrap=None, traversal_limit_in_words=None,
nesting_limit=None)
= Changes the socket parameter to be optional
= If socket is not specified, default to using a TwoWayPipe
= Simplified code by removing an else (self._connect)
+ [Added] async def read(self, bufsize)
= awaitable function that blocks until data has been read
= bufsize defines the maximum amount of data to be read back
(e.g. 4096 bytes)
= Reads data from TwoWayPipe
+ [Added] def write(self, data)
= Write data to TwoWayPipe
= Not awaitable as the write interface of the TwoWayPipe doesn't
have poll functionality
+ [Added] async def poll_forever(self)
= asyncio equivalent of run_forever()
* class _TwoWayPipe
+ Wrapper class for TwoWayPipe
Other Additions
- capnp/helpers/asyncHelper.h
* pollWaitScope
+ Pumps the kj event handler
+ Used for the TwoWayServer
* pollRemote
+ Polls a remote promise
+ i.e. a capnp RPC call
- capnp/helpers/asyncIoHelper.h
* AsyncIoStreamReadHelper
+ I wasn't able to figure out Promise[size_t] using Cython so this was
the next best thing I could think of doing
+ Was needed to handle read polling from a read promise
= Polling is used for asyncio as kj waits need a wrapper to be
compatible
- capnp/lib/capnp.pyx
* makeTwoWayPipe
+ Wrapper for kj newTwoWayPipe function
* poll_once
+ Single pump of the kj event handler (used with pollWaitScope)
TwoWayClient Usage - TwoWayPipe
- See examples/async_client.py
TwoWayServer Usage - TwoWayPipe
- See examples/async_server.py
capnp/helpers/asyncIoHelper.h
Misc Changes
- Fixed thread_server.py and thread_client.py to use bootstrap instead
of ez_restore
- async_client.py and async_server.py examples
* Uses the same thread.capnp as thread_client.py and thread_server.py
* They are compatible, so you can mix and match client and server for
compatibility testing
* async_client.py and async_server.py require <address>:<port>
formatting (unlike autodetection from thread_client.py and
thread_server.py)
This commit is contained in:
@@ -39,6 +39,11 @@ void waitNeverDone(kj::WaitScope & scope) {
|
||||
kj::NEVER_DONE.wait(scope);
|
||||
}
|
||||
|
||||
void pollWaitScope(kj::WaitScope & scope) {
|
||||
GILRelease gil;
|
||||
scope.poll();
|
||||
}
|
||||
|
||||
kj::Timer * getTimer(kj::AsyncIoContext * context) {
|
||||
return &context->lowLevelProvider->getTimer();
|
||||
}
|
||||
@@ -57,3 +62,8 @@ capnp::Response< ::capnp::DynamicStruct> * waitRemote(capnp::RemotePromise< ::ca
|
||||
GILRelease gil;
|
||||
return new capnp::Response< ::capnp::DynamicStruct>(promise->wait(scope));
|
||||
}
|
||||
|
||||
bool pollRemote(capnp::RemotePromise< ::capnp::DynamicStruct> * promise, kj::WaitScope & scope) {
|
||||
GILRelease gil;
|
||||
return promise->poll(scope);
|
||||
}
|
||||
|
||||
53
capnp/helpers/asyncIoHelper.h
Normal file
53
capnp/helpers/asyncIoHelper.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "kj/async.h"
|
||||
#include "kj/async-io.h"
|
||||
|
||||
class AsyncIoStreamReadHelper {
|
||||
public:
|
||||
AsyncIoStreamReadHelper(kj::AsyncIoStream * _stream, kj::WaitScope * _scope, size_t bufsize) {
|
||||
io_stream = _stream;
|
||||
wait_scope = _scope;
|
||||
ready = false;
|
||||
buffer_read_size = 0;
|
||||
buffer = new unsigned char[bufsize];
|
||||
promise = io_stream->read(buffer, 1, bufsize);
|
||||
}
|
||||
|
||||
~AsyncIoStreamReadHelper() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
bool poll() {
|
||||
bool result = promise.poll(*wait_scope);
|
||||
if (result) {
|
||||
ready = true;
|
||||
buffer_read_size = promise.wait(*wait_scope);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t read_size() {
|
||||
if (!ready) {
|
||||
return 0;
|
||||
}
|
||||
return buffer_read_size;
|
||||
}
|
||||
|
||||
void * read_buffer() {
|
||||
if (!ready) {
|
||||
return 0;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
kj::AsyncIoStream * io_stream;
|
||||
kj::WaitScope * wait_scope;
|
||||
kj::Promise<size_t> promise = nullptr;
|
||||
|
||||
unsigned char *buffer;
|
||||
size_t buffer_read_size;
|
||||
|
||||
bool ready;
|
||||
};
|
||||
@@ -2,10 +2,12 @@ from capnp.includes.capnp_cpp cimport Maybe, DynamicStruct, Request, Response, P
|
||||
|
||||
from capnp.includes.schema_cpp cimport ByteArray
|
||||
|
||||
from non_circular cimport reraise_kj_exception
|
||||
from non_circular cimport reraise_kj_exception, AsyncIoStreamReadHelper
|
||||
|
||||
from cpython.ref cimport PyObject
|
||||
|
||||
from libcpp cimport bool
|
||||
|
||||
cdef extern from "capnp/helpers/fixMaybe.h":
|
||||
EnumSchema.Enumerant fixMaybe(Maybe[EnumSchema.Enumerant]) except +reraise_kj_exception
|
||||
StructSchema.Field fixMaybe(Maybe[StructSchema.Field]) except +reraise_kj_exception
|
||||
@@ -41,7 +43,9 @@ cdef extern from "capnp/helpers/serialize.h":
|
||||
|
||||
cdef extern from "capnp/helpers/asyncHelper.h":
|
||||
void waitNeverDone(WaitScope&)
|
||||
void pollWaitScope(WaitScope&)
|
||||
Response * waitRemote(RemotePromise *, WaitScope&)
|
||||
bool pollRemote(RemotePromise *, WaitScope&)
|
||||
PyObject * waitPyPromise(PyPromise *, WaitScope&)
|
||||
void waitVoidPromise(VoidPromise *, WaitScope&)
|
||||
Timer * getTimer(AsyncIoContext *) except +reraise_kj_exception
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from cpython.ref cimport PyObject
|
||||
from capnp.includes.capnp_cpp cimport AsyncIoStream, WaitScope
|
||||
from libcpp cimport bool
|
||||
|
||||
cdef extern from "capnp/helpers/capabilityHelper.h":
|
||||
cppclass PythonInterfaceDynamicImpl:
|
||||
@@ -18,3 +20,10 @@ cdef extern from "capnp/helpers/rpcHelper.h":
|
||||
cdef extern from "capnp/helpers/asyncHelper.h":
|
||||
cdef cppclass PyEventPort:
|
||||
PyEventPort(PyObject *)
|
||||
|
||||
cdef extern from "capnp/helpers/asyncIoHelper.h":
|
||||
cdef cppclass AsyncIoStreamReadHelper:
|
||||
AsyncIoStreamReadHelper(AsyncIoStream *, WaitScope *, size_t)
|
||||
bool poll()
|
||||
size_t read_size()
|
||||
void* read_buffer()
|
||||
|
||||
Reference in New Issue
Block a user