Commit edbc2a5a authored by Kenton Varda's avatar Kenton Varda

IT WOOOORRRKKSS: Local capabilities, including pipelining. Now we just need an RPC protocol...

parent f566cd4d
...@@ -35,9 +35,6 @@ ...@@ -35,9 +35,6 @@
namespace capnp { namespace capnp {
namespace _ { // private namespace _ { // private
Arena::~Arena() noexcept(false) {}
BuilderArena::~BuilderArena() noexcept(false) {}
namespace { namespace {
class BrokenPipeline final: public PipelineHook, public kj::Refcounted { class BrokenPipeline final: public PipelineHook, public kj::Refcounted {
...@@ -110,10 +107,13 @@ kj::Own<const ClientHook> BrokenPipeline::getPipelinedCap( ...@@ -110,10 +107,13 @@ kj::Own<const ClientHook> BrokenPipeline::getPipelinedCap(
} // namespace } // namespace
kj::Own<const ClientHook> Arena::extractNullCap() { kj::Own<const ClientHook> newBrokenCap(const char* reason) {
return kj::refcounted<BrokenClient>("Calling null capability pointer."); return kj::refcounted<BrokenClient>(reason);
} }
Arena::~Arena() noexcept(false) {}
BuilderArena::~BuilderArena() noexcept(false) {}
void ReadLimiter::unread(WordCount64 amount) { void ReadLimiter::unread(WordCount64 amount) {
// Be careful not to overflow here. Since ReadLimiter has no thread-safety, it's possible that // Be careful not to overflow here. Since ReadLimiter has no thread-safety, it's possible that
// the limit value was not updated correctly for one or more reads, and therefore unread() could // the limit value was not updated correctly for one or more reads, and therefore unread() could
...@@ -180,7 +180,7 @@ void BasicReaderArena::reportReadLimitReached() { ...@@ -180,7 +180,7 @@ void BasicReaderArena::reportReadLimitReached() {
kj::Own<const ClientHook> BasicReaderArena::extractCap(const _::StructReader& capDescriptor) { kj::Own<const ClientHook> BasicReaderArena::extractCap(const _::StructReader& capDescriptor) {
KJ_FAIL_REQUIRE("Message contained a capability but is not imbued with a capability context.") { KJ_FAIL_REQUIRE("Message contained a capability but is not imbued with a capability context.") {
return kj::heap<BrokenClient>( return newBrokenCap(
"Calling capability extracted from message that was not imbued with a capability " "Calling capability extracted from message that was not imbued with a capability "
"context."); "context.");
} }
...@@ -406,7 +406,7 @@ SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) { ...@@ -406,7 +406,7 @@ SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) {
if (baseSegment->getSegmentId() == SegmentId(0)) { if (baseSegment->getSegmentId() == SegmentId(0)) {
if (segment0.getArena() == nullptr) { if (segment0.getArena() == nullptr) {
kj::dtor(segment0); kj::dtor(segment0);
kj::ctor(segment0, baseSegment); kj::ctor(segment0, this, baseSegment);
} }
result = &segment0; result = &segment0;
} else { } else {
...@@ -419,7 +419,7 @@ SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) { ...@@ -419,7 +419,7 @@ SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) {
KJ_IF_MAYBE(segment, segmentState->get()->builders[id]) { KJ_IF_MAYBE(segment, segmentState->get()->builders[id]) {
result = *segment; result = *segment;
} else { } else {
auto newBuilder = kj::heap<ImbuedSegmentBuilder>(baseSegment); auto newBuilder = kj::heap<ImbuedSegmentBuilder>(this, baseSegment);
result = newBuilder; result = newBuilder;
segmentState->get()->builders[id] = kj::mv(newBuilder); segmentState->get()->builders[id] = kj::mv(newBuilder);
} }
...@@ -448,7 +448,7 @@ SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) { ...@@ -448,7 +448,7 @@ SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) {
} }
BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) { BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) {
auto result = allocate(amount); auto result = base->allocate(amount);
result.segment = imbue(result.segment); result.segment = imbue(result.segment);
return result; return result;
} }
......
...@@ -57,6 +57,11 @@ class ReadLimiter; ...@@ -57,6 +57,11 @@ class ReadLimiter;
class Segment; class Segment;
typedef kj::Id<uint32_t, Segment> SegmentId; typedef kj::Id<uint32_t, Segment> SegmentId;
kj::Own<const ClientHook> newBrokenCap(const char* reason);
// Helper function that creates a capability which simply throws exceptions when called.
// Implemented in arena.c++ rather than capability.c++ because it is needed by layout.c++ and we
// don't want capability.c++ to be required by people not using caps.
class ReadLimiter { class ReadLimiter {
// Used to keep track of how much data has been processed from a message, and cut off further // Used to keep track of how much data has been processed from a message, and cut off further
// processing if and when a particular limit is reached. This is primarily intended to guard // processing if and when a particular limit is reached. This is primarily intended to guard
...@@ -167,7 +172,7 @@ private: ...@@ -167,7 +172,7 @@ private:
class ImbuedSegmentBuilder: public SegmentBuilder { class ImbuedSegmentBuilder: public SegmentBuilder {
public: public:
inline ImbuedSegmentBuilder(SegmentBuilder* base); inline ImbuedSegmentBuilder(ImbuedBuilderArena* arena, SegmentBuilder* base);
inline ImbuedSegmentBuilder(decltype(nullptr)); inline ImbuedSegmentBuilder(decltype(nullptr));
KJ_DISALLOW_COPY(ImbuedSegmentBuilder); KJ_DISALLOW_COPY(ImbuedSegmentBuilder);
...@@ -188,10 +193,6 @@ public: ...@@ -188,10 +193,6 @@ public:
virtual kj::Own<const ClientHook> extractCap(const _::StructReader& capDescriptor) = 0; virtual kj::Own<const ClientHook> extractCap(const _::StructReader& capDescriptor) = 0;
// Given a StructReader for a capability descriptor embedded in the message, return the // Given a StructReader for a capability descriptor embedded in the message, return the
// corresponding capability. // corresponding capability.
kj::Own<const ClientHook> extractNullCap();
// Like extractCap() but called when the pointer was null. This just returns a dummy capability
// that throws exceptions on any call.
}; };
class BasicReaderArena final: public Arena { class BasicReaderArena final: public Arena {
...@@ -444,8 +445,8 @@ inline BasicSegmentBuilder::BasicSegmentBuilder( ...@@ -444,8 +445,8 @@ inline BasicSegmentBuilder::BasicSegmentBuilder(
: SegmentBuilder(arena, id, ptr, readLimiter, &actualPos), : SegmentBuilder(arena, id, ptr, readLimiter, &actualPos),
actualPos(ptr.begin()) {} actualPos(ptr.begin()) {}
inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(SegmentBuilder* base) inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(ImbuedBuilderArena* arena, SegmentBuilder* base)
: SegmentBuilder(static_cast<BuilderArena*>(base->arena), base->id, : SegmentBuilder(arena, base->id,
kj::arrayPtr(const_cast<word*>(base->ptr.begin()), base->ptr.size()), kj::arrayPtr(const_cast<word*>(base->ptr.begin()), base->ptr.size()),
base->readLimiter, base->pos) {} base->readLimiter, base->pos) {}
inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(decltype(nullptr)) inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(decltype(nullptr))
......
...@@ -21,6 +21,18 @@ ...@@ -21,6 +21,18 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "schema.capnp.h"
#ifdef CAPNP_CAPABILITY_H_
#error "schema.capnp should not depend on capability.h, because it contains no interfaces."
#endif
#include "test.capnp.h"
#ifndef CAPNP_CAPABILITY_H_
#error "test.capnp did not include capability.h."
#endif
#include "capability.h" #include "capability.h"
#include "test-util.h" #include "test-util.h"
#include <kj/debug.h> #include <kj/debug.h>
...@@ -36,9 +48,9 @@ public: ...@@ -36,9 +48,9 @@ public:
int& callCount; int& callCount;
virtual ::kj::Promise<void> foo( ::kj::Promise<void> foo(
test::TestInterface::FooParams::Reader params, test::TestInterface::FooParams::Reader params,
test::TestInterface::FooResults::Builder result) { test::TestInterface::FooResults::Builder result) override {
++callCount; ++callCount;
EXPECT_EQ(123, params.getI()); EXPECT_EQ(123, params.getI());
EXPECT_TRUE(params.getJ()); EXPECT_TRUE(params.getJ());
...@@ -46,9 +58,9 @@ public: ...@@ -46,9 +58,9 @@ public:
return kj::READY_NOW; return kj::READY_NOW;
} }
virtual ::kj::Promise<void> bazAdvanced( ::kj::Promise<void> bazAdvanced(
::capnp::CallContext<test::TestInterface::BazParams, ::capnp::CallContext<test::TestInterface::BazParams,
test::TestInterface::BazResults> context) { test::TestInterface::BazResults> context) override {
++callCount; ++callCount;
auto params = context.getParams(); auto params = context.getParams();
checkTestMessage(params.getS()); checkTestMessage(params.getS());
...@@ -64,7 +76,7 @@ public: ...@@ -64,7 +76,7 @@ public:
TEST(Capability, Basic) { TEST(Capability, Basic) {
kj::SimpleEventLoop loop; kj::SimpleEventLoop loop;
int callCount; int callCount = 0;
test::TestInterface::Client client(makeLocalClient(kj::heap<TestInterfaceImpl>(callCount), loop)); test::TestInterface::Client client(makeLocalClient(kj::heap<TestInterfaceImpl>(callCount), loop));
auto request1 = client.fooRequest(); auto request1 = client.fooRequest();
...@@ -103,9 +115,9 @@ public: ...@@ -103,9 +115,9 @@ public:
int& callCount; int& callCount;
virtual ::kj::Promise<void> foo( ::kj::Promise<void> foo(
test::TestInterface::FooParams::Reader params, test::TestInterface::FooParams::Reader params,
test::TestInterface::FooResults::Builder result) { test::TestInterface::FooResults::Builder result) override {
++callCount; ++callCount;
EXPECT_EQ(321, params.getI()); EXPECT_EQ(321, params.getI());
EXPECT_FALSE(params.getJ()); EXPECT_FALSE(params.getJ());
...@@ -113,8 +125,8 @@ public: ...@@ -113,8 +125,8 @@ public:
return kj::READY_NOW; return kj::READY_NOW;
} }
virtual ::kj::Promise<void> graultAdvanced( ::kj::Promise<void> graultAdvanced(
::capnp::CallContext<test::TestExtends::GraultParams, test::TestAllTypes> context) { ::capnp::CallContext<test::TestExtends::GraultParams, test::TestAllTypes> context) override {
++callCount; ++callCount;
context.releaseParams(); context.releaseParams();
...@@ -127,7 +139,7 @@ public: ...@@ -127,7 +139,7 @@ public:
TEST(Capability, Inheritance) { TEST(Capability, Inheritance) {
kj::SimpleEventLoop loop; kj::SimpleEventLoop loop;
int callCount; int callCount = 0;
test::TestExtends::Client client(makeLocalClient(kj::heap<TestExtendsImpl>(callCount), loop)); test::TestExtends::Client client(makeLocalClient(kj::heap<TestExtendsImpl>(callCount), loop));
auto request1 = client.fooRequest(); auto request1 = client.fooRequest();
...@@ -150,6 +162,67 @@ TEST(Capability, Inheritance) { ...@@ -150,6 +162,67 @@ TEST(Capability, Inheritance) {
EXPECT_EQ(2, callCount); EXPECT_EQ(2, callCount);
} }
class TestPipelineImpl final: public test::TestPipeline::Server {
public:
TestPipelineImpl(int& callCount): callCount(callCount) {}
int& callCount;
::kj::Promise<void> getCapAdvanced(
capnp::CallContext<test::TestPipeline::GetCapParams,
test::TestPipeline::GetCapResults> context) override {
++callCount;
auto params = context.getParams();
EXPECT_EQ("foo", params.getS());
auto cap = params.getInCap();
context.releaseParams();
auto request = cap.fooRequest();
request.setI(123);
request.setJ(true);
return request.send().then(
[this,context](capnp::Response<test::TestInterface::FooResults>&& response) mutable {
EXPECT_EQ("foo", response.getX());
auto result = context.getResults();
result.setN(234);
result.setOutCap(test::TestExtends::Client(
makeLocalClient(kj::heap<TestExtendsImpl>(callCount))));
});
}
};
TEST(Capability, Pipelining) {
kj::SimpleEventLoop loop;
int callCount = 0;
int chainedCallCount = 0;
test::TestPipeline::Client client(makeLocalClient(kj::heap<TestPipelineImpl>(callCount), loop));
auto request = client.getCapRequest();
request.setS("foo");
request.setInCap(test::TestInterface::Client(
makeLocalClient(kj::heap<TestInterfaceImpl>(chainedCallCount), loop)));
auto promise = request.send();
auto pipelineRequest = promise.getOutCap().fooRequest();
pipelineRequest.setI(321);
auto pipelinePromise = pipelineRequest.send();
EXPECT_EQ(0, callCount);
EXPECT_EQ(0, chainedCallCount);
auto response = loop.wait(kj::mv(pipelinePromise));
EXPECT_EQ("bar", response.getX());
EXPECT_EQ(2, callCount);
EXPECT_EQ(1, chainedCallCount);
}
} // namespace } // namespace
} // namespace _ } // namespace _
} // namespace capnp } // namespace capnp
This diff is collapsed.
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <kj/async.h> #include <kj/async.h>
#include "object.h" #include "object.h"
#include "pointer-helpers.h"
namespace capnp { namespace capnp {
...@@ -133,6 +134,9 @@ public: ...@@ -133,6 +134,9 @@ public:
private: private:
kj::Own<const ClientHook> hook; kj::Own<const ClientHook> hook;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
protected: protected:
Client() = default; Client() = default;
...@@ -148,7 +152,7 @@ class CallContextHook; ...@@ -148,7 +152,7 @@ class CallContextHook;
template <typename Params, typename Results> template <typename Params, typename Results>
class CallContext: public kj::DisallowConstCopy { class CallContext: public kj::DisallowConstCopy {
// Wrapper around TypelessCallContext with a specific return type. // Wrapper around CallContextHook with a specific return type.
// //
// Methods of this class may only be called from within the server's event loop, not from other // Methods of this class may only be called from within the server's event loop, not from other
// threads. // threads.
...@@ -246,51 +250,6 @@ kj::Own<const ClientHook> makeLocalClient(kj::Own<Capability::Server>&& server, ...@@ -246,51 +250,6 @@ kj::Own<const ClientHook> makeLocalClient(kj::Own<Capability::Server>&& server,
// //
// TODO(now): Templated version or something. // TODO(now): Templated version or something.
// =======================================================================================
struct PipelineOp {
enum Type {
GET_POINTER_FIELD
// There may be other types in the future...
};
Type type;
union {
uint16_t pointerIndex; // for GET_POINTER_FIELD
};
};
struct TypelessResults {
// Result of a call, before it has been type-wrapped. Designed to be used as
// RemotePromise<CallResult>.
typedef ObjectPointer::Reader Reader;
// So RemotePromise<CallResult> resolves to Own<ObjectPointer::Reader>.
class Pipeline {
public:
inline explicit Pipeline(kj::Own<const PipelineHook>&& hook): hook(kj::mv(hook)) {}
Pipeline getPointerField(uint16_t pointerIndex) const;
// Return a new Promise representing a sub-object of the result. `pointerIndex` is the index
// of the sub-object within the pointer section of the result (the result must be a struct).
//
// TODO(kenton): On GCC 4.8 / Clang 3.3, use rvalue qualifiers to avoid the need for copies.
// Also make `ops` into a Vector to optimize this.
Capability::Client asCap() const;
// Expect that the result is a capability and construct a pipelined version of it now.
private:
kj::Own<const PipelineHook> hook;
kj::Array<PipelineOp> ops;
inline Pipeline(kj::Own<const PipelineHook>&& hook, kj::Array<PipelineOp>&& ops)
: hook(kj::mv(hook)), ops(kj::mv(ops)) {}
};
};
// ======================================================================================= // =======================================================================================
// Hook interfaces which must be implemented by the RPC system. Applications never call these // Hook interfaces which must be implemented by the RPC system. Applications never call these
// directly; the RPC system implements them and the types defined earlier in this file wrap them. // directly; the RPC system implements them and the types defined earlier in this file wrap them.
...@@ -299,7 +258,7 @@ class RequestHook { ...@@ -299,7 +258,7 @@ class RequestHook {
// Hook interface implemented by RPC system representing a request being built. // Hook interface implemented by RPC system representing a request being built.
public: public:
virtual RemotePromise<TypelessResults> send() = 0; virtual RemotePromise<ObjectPointer> send() = 0;
// Send the call and return a promise for the result. // Send the call and return a promise for the result.
}; };
...@@ -314,26 +273,11 @@ public: ...@@ -314,26 +273,11 @@ public:
// Just here to make sure the type is dynamic. // Just here to make sure the type is dynamic.
}; };
class PipelineHook { // class PipelineHook is declared in object.h because it is needed there.
// Represents a currently-running call, and implements pipelined requests on its result.
public:
virtual kj::Own<const PipelineHook> addRef() const = 0;
// Increment this object's reference count.
virtual kj::Own<const ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) const = 0;
// Extract a promised Capability from the results.
virtual kj::Own<const ClientHook> getPipelinedCap(kj::Array<PipelineOp>&& ops) const {
// Version of getPipelinedCap() passing the array by move. May avoid a copy in some cases.
// Default implementation just calls the other version.
return getPipelinedCap(ops.asPtr());
}
};
class ClientHook { class ClientHook {
public: public:
virtual Request<ObjectPointer, TypelessResults> newCall( virtual Request<ObjectPointer, ObjectPointer> newCall(
uint64_t interfaceId, uint16_t methodId, uint firstSegmentWordSize) const = 0; uint64_t interfaceId, uint16_t methodId, uint firstSegmentWordSize) const = 0;
// Start a new call, allowing the client to allocate request/response objects as it sees fit. // Start a new call, allowing the client to allocate request/response objects as it sees fit.
// This version is used when calls are made from application code in the local process. // This version is used when calls are made from application code in the local process.
...@@ -386,6 +330,35 @@ public: ...@@ -386,6 +330,35 @@ public:
virtual kj::Own<CallContextHook> addRef() = 0; virtual kj::Own<CallContextHook> addRef() = 0;
}; };
kj::Own<const ClientHook> newBrokenCap(const char* reason);
// Helper function that creates a capability which simply throws exceptions when called.
// =======================================================================================
// Extend PointerHelpers for interfaces
namespace _ { // private
template <typename T>
struct PointerHelpers<T, Kind::INTERFACE> {
static inline typename T::Client get(PointerReader reader) {
return typename T::Client(reader.getCapability());
}
static inline typename T::Client get(PointerBuilder builder) {
return typename T::Client(builder.getCapability());
}
static inline void set(PointerBuilder builder, typename T::Client&& value) {
builder.setCapability(kj::mv(value.Capability::Client::hook));
}
static inline void adopt(PointerBuilder builder, Orphan<T>&& value) {
builder.adopt(kj::mv(value.builder));
}
static inline Orphan<T> disown(PointerBuilder builder) {
return Orphan<T>(builder.disown());
}
};
} // namespace _ (private)
// ======================================================================================= // =======================================================================================
// Inline implementation details // Inline implementation details
...@@ -396,22 +369,18 @@ RemotePromise<Results> Request<Params, Results>::send() { ...@@ -396,22 +369,18 @@ RemotePromise<Results> Request<Params, Results>::send() {
// Convert the Promise to return the correct response type. // Convert the Promise to return the correct response type.
// Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the // Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the
// Pipeline part of the RemotePromise. // Pipeline part of the RemotePromise.
auto typedPromise = kj::implicitCast<kj::Promise<Response<TypelessResults>>&>(typelessPromise) auto typedPromise = kj::implicitCast<kj::Promise<Response<ObjectPointer>>&>(typelessPromise)
.thenInAnyThread([](Response<TypelessResults>&& response) -> Response<Results> { .thenInAnyThread([](Response<ObjectPointer>&& response) -> Response<Results> {
return Response<Results>(response.getAs<Results>(), kj::mv(response.hook)); return Response<Results>(response.getAs<Results>(), kj::mv(response.hook));
}); });
// Wrap the typeless pipeline in a typed wrapper. // Wrap the typeless pipeline in a typed wrapper.
typename Results::Pipeline typedPipeline( typename Results::Pipeline typedPipeline(
kj::mv(kj::implicitCast<TypelessResults::Pipeline&>(typelessPromise))); kj::mv(kj::implicitCast<ObjectPointer::Pipeline&>(typelessPromise)));
return RemotePromise<Results>(kj::mv(typedPromise), kj::mv(typedPipeline)); return RemotePromise<Results>(kj::mv(typedPromise), kj::mv(typedPipeline));
} }
inline Capability::Client TypelessResults::Pipeline::asCap() const {
return Capability::Client(hook->getPipelinedCap(ops));
}
inline Capability::Client::Client(kj::Own<const ClientHook>&& hook): hook(kj::mv(hook)) {} inline Capability::Client::Client(kj::Own<const ClientHook>&& hook): hook(kj::mv(hook)) {}
inline Capability::Client::Client(const Client& other): hook(other.hook->addRef()) {} inline Capability::Client::Client(const Client& other): hook(other.hook->addRef()) {}
inline Capability::Client& Capability::Client::operator=(const Client& other) { inline Capability::Client& Capability::Client::operator=(const Client& other) {
......
...@@ -110,12 +110,14 @@ template <typename T, Kind k> struct Kind_<List<T, k>> { static constexpr Kind k ...@@ -110,12 +110,14 @@ template <typename T, Kind k> struct Kind_<List<T, k>> { static constexpr Kind k
template <typename T, Kind k = kind<T>()> struct ReaderFor_ { typedef typename T::Reader Type; }; template <typename T, Kind k = kind<T>()> struct ReaderFor_ { typedef typename T::Reader Type; };
template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; }; template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; }; template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
template <typename T> using ReaderFor = typename ReaderFor_<T>::Type; template <typename T> using ReaderFor = typename ReaderFor_<T>::Type;
// The type returned by List<T>::Reader::operator[]. // The type returned by List<T>::Reader::operator[].
template <typename T, Kind k = kind<T>()> struct BuilderFor_ { typedef typename T::Builder Type; }; template <typename T, Kind k = kind<T>()> struct BuilderFor_ { typedef typename T::Builder Type; };
template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; }; template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; }; template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::INTERFACE> { typedef typename T::Client Type; };
template <typename T> using BuilderFor = typename BuilderFor_<T>::Type; template <typename T> using BuilderFor = typename BuilderFor_<T>::Type;
// The type returned by List<T>::Builder::operator[]. // The type returned by List<T>::Builder::operator[].
......
...@@ -571,7 +571,8 @@ private: ...@@ -571,7 +571,8 @@ private:
" inline ", titleCase, "::Builder init", titleCase, "();\n" " inline ", titleCase, "::Builder init", titleCase, "();\n"
"\n"), "\n"),
kj::strTree(), proto.hasDiscriminantValue() ? kj::strTree() :
kj::strTree(" inline ", titleCase, "::Pipeline get", titleCase, "() const;\n"),
kj::strTree( kj::strTree(
kj::mv(unionDiscrim.isDefs), kj::mv(unionDiscrim.isDefs),
...@@ -620,7 +621,11 @@ private: ...@@ -620,7 +621,11 @@ private:
"inline ", scope, titleCase, "::Builder ", scope, "Builder::get", titleCase, "() {\n", "inline ", scope, titleCase, "::Builder ", scope, "Builder::get", titleCase, "() {\n",
unionDiscrim.check, unionDiscrim.check,
" return ", scope, titleCase, "::Builder(_builder);\n" " return ", scope, titleCase, "::Builder(_builder);\n"
"}\n" "}\n",
proto.hasDiscriminantValue() ? kj::strTree() : kj::strTree(
"inline ", scope, titleCase, "::Pipeline ", scope, "Pipeline::get", titleCase, "() const {\n",
" return ", scope, titleCase, "::Pipeline(_typeless.noop());\n"
"}\n"),
"inline ", scope, titleCase, "::Builder ", scope, "Builder::init", titleCase, "() {\n", "inline ", scope, titleCase, "::Builder ", scope, "Builder::init", titleCase, "() {\n",
unionDiscrim.set, unionDiscrim.set,
KJ_MAP(slot, slots) { KJ_MAP(slot, slots) {
...@@ -810,8 +815,68 @@ private: ...@@ -810,8 +815,68 @@ private:
}; };
} else if (kind == FieldKind::INTERFACE) { } else if (kind == FieldKind::INTERFACE) {
// Not implemented. return FieldText {
return FieldText { kj::strTree(), kj::strTree(), kj::strTree(), kj::strTree() }; kj::strTree(
kj::mv(unionDiscrim.readerIsDecl),
" inline bool has", titleCase, "() const;\n"
" inline ", type, "::Client get", titleCase, "() const;\n"
"\n"),
kj::strTree(
kj::mv(unionDiscrim.builderIsDecl),
" inline bool has", titleCase, "();\n"
" inline ", type, "::Client get", titleCase, "();\n"
" inline void set", titleCase, "(", type, "::Client&& value);\n",
" inline void adopt", titleCase, "(::capnp::Orphan<", type, ">&& value);\n"
" inline ::capnp::Orphan<", type, "> disown", titleCase, "();\n"
"\n"),
kj::strTree(
proto.hasDiscriminantValue() ? kj::strTree() : kj::strTree(
" inline ", type, "::Client get", titleCase, "() const;\n")),
kj::strTree(
kj::mv(unionDiscrim.isDefs),
"inline bool ", scope, "Reader::has", titleCase, "() const {\n",
unionDiscrim.has,
" return !_reader.getPointerField(", offset, " * ::capnp::POINTERS).isNull();\n"
"}\n"
"inline bool ", scope, "Builder::has", titleCase, "() {\n",
unionDiscrim.has,
" return !_builder.getPointerField(", offset, " * ::capnp::POINTERS).isNull();\n"
"}\n"
"inline ", type, "::Client ", scope, "Reader::get", titleCase, "() const {\n",
unionDiscrim.check,
" return ::capnp::_::PointerHelpers<", type, ">::get(\n"
" _reader.getPointerField(", offset, " * ::capnp::POINTERS));\n"
"}\n"
"inline ", type, "::Client ", scope, "Builder::get", titleCase, "() {\n",
unionDiscrim.check,
" return ::capnp::_::PointerHelpers<", type, ">::get(\n"
" _builder.getPointerField(", offset, " * ::capnp::POINTERS));\n"
"}\n",
proto.hasDiscriminantValue() ? kj::strTree() : kj::strTree(
"inline ", type, "::Client ", scope, "Pipeline::get", titleCase, "() const {\n",
" return ", type, "::Client(_typeless.getPointerField(", offset, ").asCap());\n"
"}\n"),
"inline void ", scope, "Builder::set", titleCase, "(", type, "::Client&& cap) {\n",
unionDiscrim.set,
" ::capnp::_::PointerHelpers<", type, ">::set(\n"
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), kj::mv(cap));\n"
"}\n",
"inline void ", scope, "Builder::adopt", titleCase, "(\n"
" ::capnp::Orphan<", type, ">&& value) {\n",
unionDiscrim.set,
" ::capnp::_::PointerHelpers<", type, ">::adopt(\n"
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), kj::mv(value));\n"
"}\n"
"inline ::capnp::Orphan<", type, "> ", scope, "Builder::disown", titleCase, "() {\n",
unionDiscrim.check,
" return ::capnp::_::PointerHelpers<", type, ">::disown(\n"
" _builder.getPointerField(", offset, " * ::capnp::POINTERS));\n"
"}\n"
"\n")
};
} else if (kind == FieldKind::OBJECT) { } else if (kind == FieldKind::OBJECT) {
return FieldText { return FieldText {
...@@ -932,7 +997,11 @@ private: ...@@ -932,7 +997,11 @@ private:
" inline ::capnp::Orphan<", type, "> disown", titleCase, "();\n" " inline ::capnp::Orphan<", type, "> disown", titleCase, "();\n"
"\n"), "\n"),
kj::strTree(), kj::strTree(
kind == FieldKind::STRUCT && !proto.hasDiscriminantValue()
? kj::strTree(
" inline ", type, "::Pipeline get", titleCase, "() const;\n")
: kj::strTree()),
kj::strTree( kj::strTree(
kj::mv(unionDiscrim.isDefs), kj::mv(unionDiscrim.isDefs),
...@@ -953,7 +1022,13 @@ private: ...@@ -953,7 +1022,13 @@ private:
unionDiscrim.check, unionDiscrim.check,
" return ::capnp::_::PointerHelpers<", type, ">::get(\n" " return ::capnp::_::PointerHelpers<", type, ">::get(\n"
" _builder.getPointerField(", offset, " * ::capnp::POINTERS)", defaultParam, ");\n" " _builder.getPointerField(", offset, " * ::capnp::POINTERS)", defaultParam, ");\n"
"}\n" "}\n",
kind == FieldKind::STRUCT && !proto.hasDiscriminantValue()
? kj::strTree(
"inline ", type, "::Pipeline ", scope, "Pipeline::get", titleCase, "() const {\n",
" return ", type, "::Pipeline(_typeless.getPointerField(", offset, "));\n"
"}\n")
: kj::strTree(),
"inline void ", scope, "Builder::set", titleCase, "(", type, "::Reader value) {\n", "inline void ", scope, "Builder::set", titleCase, "(", type, "::Reader value) {\n",
unionDiscrim.set, unionDiscrim.set,
" ::capnp::_::PointerHelpers<", type, ">::set(\n" " ::capnp::_::PointerHelpers<", type, ">::set(\n"
...@@ -1079,12 +1154,12 @@ private: ...@@ -1079,12 +1154,12 @@ private:
"public:\n" "public:\n"
" typedef ", unqualifiedParentType, " Pipelines;\n" " typedef ", unqualifiedParentType, " Pipelines;\n"
"\n" "\n"
" inline explicit Pipeline(::capnp::TypelessResults::Pipeline&& typeless)\n" " inline explicit Pipeline(::capnp::ObjectPointer::Pipeline&& typeless)\n"
" : _typeless(kj::mv(typeless)) {}\n" " : _typeless(kj::mv(typeless)) {}\n"
"\n", "\n",
kj::mv(methodDecls), kj::mv(methodDecls),
"private:\n" "private:\n"
" ::capnp::TypelessResults::Pipeline _typeless;\n" " ::capnp::ObjectPointer::Pipeline _typeless;\n"
" template <typename T, ::capnp::Kind k>\n" " template <typename T, ::capnp::Kind k>\n"
" friend struct ::capnp::ToDynamic_;\n" " friend struct ::capnp::ToDynamic_;\n"
"};\n" "};\n"
...@@ -1263,7 +1338,7 @@ private: ...@@ -1263,7 +1338,7 @@ private:
return kj::strTree(",\n public virtual ", e.typeName, "::Client"); return kj::strTree(",\n public virtual ", e.typeName, "::Client");
}, " {\n" }, " {\n"
"public:\n" "public:\n"
" inline Client(::kj::Own<const ::capnp::ClientHook>&& hook)\n" " inline explicit Client(::kj::Own<const ::capnp::ClientHook>&& hook)\n"
" : ::capnp::Capability::Client(::kj::mv(hook)) {}\n" " : ::capnp::Capability::Client(::kj::mv(hook)) {}\n"
"\n", "\n",
KJ_MAP(m, methods) { return kj::mv(m.clientDecls); }, KJ_MAP(m, methods) { return kj::mv(m.clientDecls); },
......
This diff is collapsed.
...@@ -19,6 +19,7 @@ struct Token { ...@@ -19,6 +19,7 @@ struct Token {
class Reader; class Reader;
class Builder; class Builder;
class Pipeline;
enum Which: uint16_t { enum Which: uint16_t {
IDENTIFIER, IDENTIFIER,
STRING_LITERAL, STRING_LITERAL,
...@@ -35,6 +36,7 @@ struct Statement { ...@@ -35,6 +36,7 @@ struct Statement {
class Reader; class Reader;
class Builder; class Builder;
class Pipeline;
enum Which: uint16_t { enum Which: uint16_t {
LINE, LINE,
BLOCK, BLOCK,
...@@ -46,6 +48,7 @@ struct LexedTokens { ...@@ -46,6 +48,7 @@ struct LexedTokens {
class Reader; class Reader;
class Builder; class Builder;
class Pipeline;
}; };
struct LexedStatements { struct LexedStatements {
...@@ -53,6 +56,7 @@ struct LexedStatements { ...@@ -53,6 +56,7 @@ struct LexedStatements {
class Reader; class Reader;
class Builder; class Builder;
class Pipeline;
}; };
} // namespace } // namespace
...@@ -241,6 +245,19 @@ inline ::kj::StringTree KJ_STRINGIFY(Token::Builder builder) { ...@@ -241,6 +245,19 @@ inline ::kj::StringTree KJ_STRINGIFY(Token::Builder builder) {
return ::capnp::_::structString<Token>(builder._builder.asReader()); return ::capnp::_::structString<Token>(builder._builder.asReader());
} }
class Token::Pipeline {
public:
typedef Token Pipelines;
inline explicit Pipeline(::capnp::ObjectPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::ObjectPointer::Pipeline _typeless;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
};
class Statement::Reader { class Statement::Reader {
public: public:
typedef Statement Reads; typedef Statement Reads;
...@@ -351,6 +368,19 @@ inline ::kj::StringTree KJ_STRINGIFY(Statement::Builder builder) { ...@@ -351,6 +368,19 @@ inline ::kj::StringTree KJ_STRINGIFY(Statement::Builder builder) {
return ::capnp::_::structString<Statement>(builder._builder.asReader()); return ::capnp::_::structString<Statement>(builder._builder.asReader());
} }
class Statement::Pipeline {
public:
typedef Statement Pipelines;
inline explicit Pipeline(::capnp::ObjectPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::ObjectPointer::Pipeline _typeless;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
};
class LexedTokens::Reader { class LexedTokens::Reader {
public: public:
typedef LexedTokens Reads; typedef LexedTokens Reads;
...@@ -414,6 +444,19 @@ inline ::kj::StringTree KJ_STRINGIFY(LexedTokens::Builder builder) { ...@@ -414,6 +444,19 @@ inline ::kj::StringTree KJ_STRINGIFY(LexedTokens::Builder builder) {
return ::capnp::_::structString<LexedTokens>(builder._builder.asReader()); return ::capnp::_::structString<LexedTokens>(builder._builder.asReader());
} }
class LexedTokens::Pipeline {
public:
typedef LexedTokens Pipelines;
inline explicit Pipeline(::capnp::ObjectPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::ObjectPointer::Pipeline _typeless;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
};
class LexedStatements::Reader { class LexedStatements::Reader {
public: public:
typedef LexedStatements Reads; typedef LexedStatements Reads;
...@@ -477,6 +520,19 @@ inline ::kj::StringTree KJ_STRINGIFY(LexedStatements::Builder builder) { ...@@ -477,6 +520,19 @@ inline ::kj::StringTree KJ_STRINGIFY(LexedStatements::Builder builder) {
return ::capnp::_::structString<LexedStatements>(builder._builder.asReader()); return ::capnp::_::structString<LexedStatements>(builder._builder.asReader());
} }
class LexedStatements::Pipeline {
public:
typedef LexedStatements Pipelines;
inline explicit Pipeline(::capnp::ObjectPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::ObjectPointer::Pipeline _typeless;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
};
// ======================================================================================= // =======================================================================================
inline Token::Which Token::Reader::which() const { inline Token::Which Token::Reader::which() const {
......
...@@ -1798,7 +1798,7 @@ struct WireHelpers { ...@@ -1798,7 +1798,7 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(kj::Own<const ClientHook> readCapabilityPointer( static KJ_ALWAYS_INLINE(kj::Own<const ClientHook> readCapabilityPointer(
SegmentReader* segment, const WirePointer* ref, const word* refTarget, int nestingLimit)) { SegmentReader* segment, const WirePointer* ref, const word* refTarget, int nestingLimit)) {
if (ref->isNull()) { if (ref->isNull()) {
return segment->getArena()->extractNullCap(); return newBrokenCap("Calling null capability pointer.");
} else { } else {
return segment->getArena()->extractCap(readStructOrCapDescPointer( return segment->getArena()->extractCap(readStructOrCapDescPointer(
WirePointer::CAPABILITY, segment, ref, refTarget, nullptr, nestingLimit)); WirePointer::CAPABILITY, segment, ref, refTarget, nullptr, nestingLimit));
......
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
namespace capnp { namespace capnp {
kj::Own<const ClientHook> PipelineHook::getPipelinedCap(kj::Array<PipelineOp>&& ops) const {
return getPipelinedCap(ops.asPtr());
}
kj::Own<const ClientHook> ObjectPointer::Reader::getPipelinedCap( kj::Own<const ClientHook> ObjectPointer::Reader::getPipelinedCap(
kj::ArrayPtr<const PipelineOp> ops) const { kj::ArrayPtr<const PipelineOp> ops) const {
_::PointerReader pointer = reader; _::PointerReader pointer = reader;
...@@ -41,4 +45,29 @@ kj::Own<const ClientHook> ObjectPointer::Reader::getPipelinedCap( ...@@ -41,4 +45,29 @@ kj::Own<const ClientHook> ObjectPointer::Reader::getPipelinedCap(
return pointer.getCapability(); return pointer.getCapability();
} }
ObjectPointer::Pipeline ObjectPointer::Pipeline::noop() const {
auto newOps = kj::heapArray<PipelineOp>(ops.size());
for (auto i: kj::indices(ops)) {
newOps[i] = ops[i];
}
return Pipeline(hook->addRef(), kj::mv(newOps));
}
ObjectPointer::Pipeline ObjectPointer::Pipeline::getPointerField(
uint16_t pointerIndex) const {
auto newOps = kj::heapArray<PipelineOp>(ops.size() + 1);
for (auto i: kj::indices(ops)) {
newOps[i] = ops[i];
}
auto& newOp = newOps[ops.size()];
newOp.type = PipelineOp::GET_POINTER_FIELD;
newOp.pointerIndex = pointerIndex;
return Pipeline(hook->addRef(), kj::mv(newOps));
}
kj::Own<const ClientHook> ObjectPointer::Pipeline::asCap() const {
return hook->getPipelinedCap(ops);
}
} // namespace capnp } // namespace capnp
...@@ -33,8 +33,47 @@ namespace capnp { ...@@ -33,8 +33,47 @@ namespace capnp {
class StructSchema; class StructSchema;
class ListSchema; class ListSchema;
class Orphanage; class Orphanage;
struct PipelineOp;
class ClientHook; class ClientHook;
class PipelineHook;
// =======================================================================================
// Pipeline helpers
//
// These relate to capabilities, but we don't declare them in capability.h because generated code
// for structs needs to know about these, even in files that contain no interfaces.
struct PipelineOp {
// Corresponds to rpc.capnp's PromisedAnswer.Op.
enum Type {
GET_POINTER_FIELD
// There may be other types in the future...
};
Type type;
union {
uint16_t pointerIndex; // for GET_POINTER_FIELD
};
};
class PipelineHook {
// Represents a currently-running call, and implements pipelined requests on its result.
public:
virtual kj::Own<const PipelineHook> addRef() const = 0;
// Increment this object's reference count.
virtual kj::Own<const ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) const = 0;
// Extract a promised Capability from the results.
virtual kj::Own<const ClientHook> getPipelinedCap(kj::Array<PipelineOp>&& ops) const;
// Version of getPipelinedCap() passing the array by move. May avoid a copy in some cases.
// Default implementation just calls the other version.
};
// =======================================================================================
// ObjectPointer!
struct ObjectPointer { struct ObjectPointer {
// Reader/Builder for the `Object` field type, i.e. a pointer that can point to an arbitrary // Reader/Builder for the `Object` field type, i.e. a pointer that can point to an arbitrary
...@@ -152,8 +191,36 @@ struct ObjectPointer { ...@@ -152,8 +191,36 @@ struct ObjectPointer {
_::PointerBuilder builder; _::PointerBuilder builder;
friend class CapBuilderContext; friend class CapBuilderContext;
}; };
class Pipeline {
public:
inline explicit Pipeline(kj::Own<const PipelineHook>&& hook): hook(kj::mv(hook)) {}
Pipeline noop() const;
// Just make a copy.
Pipeline getPointerField(uint16_t pointerIndex) const;
// Return a new Promise representing a sub-object of the result. `pointerIndex` is the index
// of the sub-object within the pointer section of the result (the result must be a struct).
//
// TODO(kenton): On GCC 4.8 / Clang 3.3, use rvalue qualifiers to avoid the need for copies.
// Also make `ops` into a Vector to optimize this.
kj::Own<const ClientHook> asCap() const;
// Expect that the result is a capability and construct a pipelined version of it now.
private:
kj::Own<const PipelineHook> hook;
kj::Array<PipelineOp> ops;
inline Pipeline(kj::Own<const PipelineHook>&& hook, kj::Array<PipelineOp>&& ops)
: hook(kj::mv(hook)), ops(kj::mv(ops)) {}
};
}; };
// TODO(now): delete
typedef ObjectPointer TypelessResults;
template <> template <>
class Orphan<ObjectPointer> { class Orphan<ObjectPointer> {
// An orphaned object of unknown type. // An orphaned object of unknown type.
......
...@@ -52,14 +52,14 @@ public: ...@@ -52,14 +52,14 @@ public:
Orphan(Orphan&&) = default; Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default; Orphan& operator=(Orphan&&) = default;
inline typename T::Builder get(); inline BuilderFor<T> get();
// Get the underlying builder. If the orphan is null, this will allocate and return a default // Get the underlying builder. If the orphan is null, this will allocate and return a default
// object rather than crash. This is done for security -- otherwise, you might enable a DoS // object rather than crash. This is done for security -- otherwise, you might enable a DoS
// attack any time you disown a field and fail to check if it is null. In the case of structs, // attack any time you disown a field and fail to check if it is null. In the case of structs,
// this means that the orphan is no longer null after get() returns. In the case of lists, // this means that the orphan is no longer null after get() returns. In the case of lists,
// no actual object is allocated since a simple empty ListBuilder can be returned. // no actual object is allocated since a simple empty ListBuilder can be returned.
inline typename T::Reader getReader() const; inline ReaderFor<T> getReader() const;
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; } inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; } inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
...@@ -198,12 +198,12 @@ struct OrphanGetImpl<Data, Kind::BLOB> { ...@@ -198,12 +198,12 @@ struct OrphanGetImpl<Data, Kind::BLOB> {
} // namespace _ (private) } // namespace _ (private)
template <typename T> template <typename T>
inline typename T::Builder Orphan<T>::get() { inline BuilderFor<T> Orphan<T>::get() {
return _::OrphanGetImpl<T>::apply(builder); return _::OrphanGetImpl<T>::apply(builder);
} }
template <typename T> template <typename T>
inline typename T::Reader Orphan<T>::getReader() const { inline ReaderFor<T> Orphan<T>::getReader() const {
return _::OrphanGetImpl<T>::applyReader(builder); return _::OrphanGetImpl<T>::applyReader(builder);
} }
......
This diff is collapsed.
...@@ -584,3 +584,7 @@ interface TestExtends extends(TestInterface) { ...@@ -584,3 +584,7 @@ interface TestExtends extends(TestInterface) {
corge @1 TestAllTypes -> (); corge @1 TestAllTypes -> ();
grault @2 () -> TestAllTypes; grault @2 () -> TestAllTypes;
} }
interface TestPipeline {
getCap @0 (s: Text, inCap :TestInterface) -> (n :UInt32, outCap :TestExtends);
}
...@@ -118,7 +118,9 @@ void EventLoop::waitImpl(Own<_::PromiseNode> node, _::ExceptionOrValue& result) ...@@ -118,7 +118,9 @@ void EventLoop::waitImpl(Own<_::PromiseNode> node, _::ExceptionOrValue& result)
EventLoop::Event::~Event() noexcept(false) { EventLoop::Event::~Event() noexcept(false) {
if (this != &loop.queue) { if (this != &loop.queue) {
KJ_ASSERT(next == nullptr || std::uncaught_exception(), "Event destroyed while armed."); KJ_ASSERT(next == nullptr || std::uncaught_exception(),
"Event destroyed while armed. You must call disarm() in the subclass's destructor "
"in order to ensure that fire() is not running when the event is destroyed.");
} }
} }
...@@ -393,7 +395,9 @@ ForkHubBase::ForkHubBase(const EventLoop& loop, Own<PromiseNode>&& inner, ...@@ -393,7 +395,9 @@ ForkHubBase::ForkHubBase(const EventLoop& loop, Own<PromiseNode>&& inner,
arm(YIELD); arm(YIELD);
} }
ForkHubBase::~ForkHubBase() noexcept(false) {} ForkHubBase::~ForkHubBase() noexcept(false) {
disarm();
}
void ForkHubBase::fire() { void ForkHubBase::fire() {
if (!isWaiting && !inner->onReady(*this)) { if (!isWaiting && !inner->onReady(*this)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment