Make pycapnp GIL friendly

Now calling `wait` from one thread will not block all threads
This commit is contained in:
Jason Paryani
2014-07-09 00:49:35 -07:00
parent 5befda532f
commit a1f7d32853
8 changed files with 171 additions and 26 deletions

View File

@@ -2,6 +2,7 @@
#include "kj/async.h"
#include "Python.h"
#include "capabilityHelper.h"
class PyEventPort: public kj::EventPort {
public:
@@ -10,14 +11,17 @@ public:
// Py_INCREF(py_event_port);
}
virtual void wait() {
GILAcquire gil;
PyObject_CallMethod(py_event_port, const_cast<char *>("wait"), NULL);
}
virtual void poll() {
GILAcquire gil;
PyObject_CallMethod(py_event_port, const_cast<char *>("poll"), NULL);
}
virtual void setRunnable(bool runnable) {
GILAcquire gil;
PyObject * arg = Py_False;
if (runnable)
arg = Py_True;
@@ -29,9 +33,25 @@ private:
};
void waitNeverDone(kj::WaitScope & scope) {
GILRelease gil;
kj::NEVER_DONE.wait(scope);
}
kj::Timer * getTimer(kj::AsyncIoContext * context) {
return &context->lowLevelProvider->getTimer();
}
void waitVoidPromise(kj::Promise<void> * promise, kj::WaitScope & scope) {
GILRelease gil;
promise->wait(scope);
}
PyObject * waitPyPromise(kj::Promise<PyObject *> * promise, kj::WaitScope & scope) {
GILRelease gil;
return promise->wait(scope);
}
capnp::Response< ::capnp::DynamicStruct> * waitRemote(capnp::RemotePromise< ::capnp::DynamicStruct> * promise, kj::WaitScope & scope) {
GILRelease gil;
return new capnp::Response< ::capnp::DynamicStruct>(promise->wait(scope));
}

View File

@@ -3,7 +3,6 @@
#include "capnp/dynamic.h"
#include <stdexcept>
#include "Python.h"
#include <iostream>
extern "C" {
PyObject * wrap_remote_call(PyObject * func, capnp::Response<capnp::DynamicStruct> &);
@@ -17,12 +16,38 @@ extern "C" {
::capnp::RemotePromise< ::capnp::DynamicStruct> * extract_remote_promise(PyObject *);
}
class GILAcquire {
public:
GILAcquire() : gstate(PyGILState_Ensure()) {}
~GILAcquire() {
PyGILState_Release(gstate);
}
PyGILState_STATE gstate;
};
class GILRelease {
public:
GILRelease() {
Py_UNBLOCK_THREADS
}
~GILRelease() {
Py_BLOCK_THREADS
}
PyThreadState *_save; // The macros above read/write from this variable
};
::kj::Promise<PyObject *> convert_to_pypromise(capnp::RemotePromise<capnp::DynamicStruct> & promise) {
return promise.then([](capnp::Response<capnp::DynamicStruct>&& response) { return wrap_dynamic_struct_reader(response); } );
}
::kj::Promise<PyObject *> convert_to_pypromise(kj::Promise<void> & promise) {
return promise.then([]() { Py_RETURN_NONE;} );
return promise.then([]() {
GILAcquire gil;
Py_INCREF( Py_None );
return Py_None;
});
}
template<class T>
@@ -31,6 +56,7 @@ template<class T>
}
void reraise_kj_exception() {
GILAcquire gil;
try {
if (PyErr_Occurred())
; // let the latest Python exn pass through and ignore the current one
@@ -51,6 +77,7 @@ void reraise_kj_exception() {
}
void check_py_error() {
GILAcquire gil;
PyObject * err = PyErr_Occurred();
if(err) {
PyObject * ptype, *pvalue, *ptraceback;
@@ -80,6 +107,7 @@ void check_py_error() {
}
kj::Promise<PyObject *> wrapPyFunc(PyObject * func, PyObject * arg) {
GILAcquire gil;
auto arg_promise = extract_promise(arg);
if(arg_promise == NULL) {
@@ -102,6 +130,7 @@ kj::Promise<PyObject *> wrapPyFunc(PyObject * func, PyObject * arg) {
}
kj::Promise<PyObject *> wrapPyFuncNoArg(PyObject * func) {
GILAcquire gil;
PyObject * result = PyObject_CallFunctionObjArgs(func, NULL);
check_py_error();
@@ -116,6 +145,7 @@ kj::Promise<PyObject *> wrapPyFuncNoArg(PyObject * func) {
}
kj::Promise<PyObject *> wrapRemoteCall(PyObject * func, capnp::Response<capnp::DynamicStruct> & arg) {
GILAcquire gil;
PyObject * ret = wrap_remote_call(func, arg);
check_py_error();
@@ -163,10 +193,12 @@ public:
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema, PyObject * _py_server)
: capnp::DynamicCapability::Server(schema), py_server(_py_server) {
GILAcquire gil;
Py_INCREF(_py_server);
}
~PythonInterfaceDynamicImpl() {
GILAcquire gil;
Py_DECREF(py_server);
}
@@ -192,14 +224,17 @@ public:
PyObject * obj;
PyRefCounter(PyObject * o) : obj(o) {
GILAcquire gil;
Py_INCREF(obj);
}
PyRefCounter(const PyRefCounter & ref) : obj(ref.obj) {
GILAcquire gil;
Py_INCREF(obj);
}
~PyRefCounter() {
GILAcquire gil;
Py_DECREF(obj);
}
};

View File

@@ -1,4 +1,4 @@
from .capnp.includes.capnp_cpp cimport Maybe, DynamicStruct, Request, PyPromise, VoidPromise, PyPromiseArray, RemotePromise, DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability, RpcSystem, MessageBuilder, MessageReader, TwoPartyVatNetwork, PyRestorer, AnyPointer, DynamicStruct_Builder, WaitScope, AsyncIoContext, StringPtr, TaskSet, Timer
from .capnp.includes.capnp_cpp cimport Maybe, DynamicStruct, Request, Response, PyPromise, VoidPromise, PyPromiseArray, RemotePromise, DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability, RpcSystem, MessageBuilder, MessageReader, TwoPartyVatNetwork, PyRestorer, AnyPointer, DynamicStruct_Builder, WaitScope, AsyncIoContext, StringPtr, TaskSet, Timer
from .capnp.includes.schema_cpp cimport ByteArray
@@ -38,4 +38,7 @@ cdef extern from "../helpers/serialize.h":
cdef extern from "../helpers/asyncHelper.h":
void waitNeverDone(WaitScope&)
Response * waitRemote(RemotePromise *, WaitScope&)
PyObject * waitPyPromise(PyPromise *, WaitScope&)
void waitVoidPromise(VoidPromise *, WaitScope&)
Timer * getTimer(AsyncIoContext *) except +reraise_kj_exception

View File

@@ -22,6 +22,7 @@ public:
// }
capnp::Capability::Client restore(capnp::AnyPointer::Reader objectId) override {
GILAcquire gil;
capnp::Capability::Client * ret = call_py_restorer(py_restorer, objectId);
check_py_error();
capnp::Capability::Client stack_ret(*ret);
@@ -113,17 +114,17 @@ void acceptLoop(kj::TaskSet & tasks, PyRestorer & restorer, kj::Own<kj::Connecti
}
kj::Promise<PyObject *> connectServer(kj::TaskSet & tasks, PyRestorer & restorer, kj::AsyncIoContext * context, kj::StringPtr bindAddress) {
auto paf = kj::newPromiseAndFulfiller<uint>();
auto paf = kj::newPromiseAndFulfiller<unsigned int>();
auto portPromise = paf.promise.fork();
tasks.add(context->provider->getNetwork().parseAddress(bindAddress)
.then(kj::mvCapture(paf.fulfiller,
[&](kj::Own<kj::PromiseFulfiller<uint>>&& portFulfiller,
[&](kj::Own<kj::PromiseFulfiller<unsigned int>>&& portFulfiller,
kj::Own<kj::NetworkAddress>&& addr) {
auto listener = addr->listen();
portFulfiller->fulfill(listener->getPort());
acceptLoop(tasks, restorer, kj::mv(listener));
})));
return portPromise.addBranch().then([&](uint port) { return PyLong_FromUnsignedLong(port); });
return portPromise.addBranch().then([&](unsigned int port) { return PyLong_FromUnsignedLong(port); });
}