Commit d07bf3b8 authored by Kenton Varda's avatar Kenton Varda

More capabilities core API WIP.

parent 398dac0d
...@@ -24,10 +24,13 @@ ...@@ -24,10 +24,13 @@
#define CAPNP_PRIVATE #define CAPNP_PRIVATE
#include "arena.h" #include "arena.h"
#include "message.h" #include "message.h"
#include "capability.h"
#include <kj/debug.h> #include <kj/debug.h>
#include <vector> #include <vector>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "capability.h"
#include "capability-context.h"
namespace capnp { namespace capnp {
namespace _ { // private namespace _ { // private
...@@ -99,10 +102,42 @@ void BasicReaderArena::reportReadLimitReached() { ...@@ -99,10 +102,42 @@ void BasicReaderArena::reportReadLimitReached() {
} }
} }
namespace {
class DummyClientHook final: public ClientHook {
public:
Request<ObjectPointer, TypelessAnswer> newCall(
uint64_t interfaceId, uint16_t methodId) const override {
KJ_FAIL_REQUIRE("Calling capability that was extracted from a message that had no "
"capability context.");
}
kj::Promise<void> whenResolved() const override {
return kj::READY_NOW;
}
kj::Own<ClientHook> addRef() const override {
return kj::heap<DummyClientHook>();
}
void* getBrand() const override {
return nullptr;
}
};
} // namespace
kj::Own<ClientHook> BasicReaderArena::extractCap(const _::StructReader& capDescriptor) {
KJ_FAIL_REQUIRE("Message contained a capability but is not imbued with a capability context.") {
return kj::heap<DummyClientHook>();
}
}
// ======================================================================================= // =======================================================================================
ImbuedReaderArena::ImbuedReaderArena(Arena* base) ImbuedReaderArena::ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor)
: base(base), segment0(nullptr, SegmentId(0), nullptr, nullptr) {} : base(base), capExtractor(capExtractor),
segment0(nullptr, SegmentId(0), nullptr, nullptr) {}
ImbuedReaderArena::~ImbuedReaderArena() noexcept(false) {} ImbuedReaderArena::~ImbuedReaderArena() noexcept(false) {}
SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) { SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) {
...@@ -139,8 +174,6 @@ SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) { ...@@ -139,8 +174,6 @@ SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) {
return result; return result;
} }
// implements Arena ------------------------------------------------
SegmentReader* ImbuedReaderArena::tryGetSegment(SegmentId id) { SegmentReader* ImbuedReaderArena::tryGetSegment(SegmentId id) {
return imbue(base->tryGetSegment(id)); return imbue(base->tryGetSegment(id));
} }
...@@ -149,6 +182,10 @@ void ImbuedReaderArena::reportReadLimitReached() { ...@@ -149,6 +182,10 @@ void ImbuedReaderArena::reportReadLimitReached() {
return base->reportReadLimitReached(); return base->reportReadLimitReached();
} }
kj::Own<ClientHook> ImbuedReaderArena::extractCap(const _::StructReader& capDescriptor) {
return capExtractor->extractCapInternal(capDescriptor);
}
// ======================================================================================= // =======================================================================================
BasicBuilderArena::BasicBuilderArena(MessageBuilder* message) BasicBuilderArena::BasicBuilderArena(MessageBuilder* message)
...@@ -288,10 +325,19 @@ void BasicBuilderArena::reportReadLimitReached() { ...@@ -288,10 +325,19 @@ void BasicBuilderArena::reportReadLimitReached() {
} }
} }
kj::Own<ClientHook> BasicBuilderArena::extractCap(const _::StructReader& capDescriptor) {
KJ_FAIL_REQUIRE("Message contains no capabilities.");
}
void BasicBuilderArena::injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) {
KJ_FAIL_REQUIRE("Cannot inject capability into a builder that has not been imbued with a "
"capability context.");
}
// ======================================================================================= // =======================================================================================
ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base) ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector)
: base(base), segment0(nullptr) {} : base(base), capInjector(capInjector), segment0(nullptr) {}
ImbuedBuilderArena::~ImbuedBuilderArena() noexcept(false) {} ImbuedBuilderArena::~ImbuedBuilderArena() noexcept(false) {}
SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) { SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) {
...@@ -334,6 +380,10 @@ void ImbuedBuilderArena::reportReadLimitReached() { ...@@ -334,6 +380,10 @@ void ImbuedBuilderArena::reportReadLimitReached() {
base->reportReadLimitReached(); base->reportReadLimitReached();
} }
kj::Own<ClientHook> ImbuedBuilderArena::extractCap(const _::StructReader& capDescriptor) {
return capInjector->getInjectedCapInternal(capDescriptor);
}
SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) { SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) {
return imbue(base->getSegment(id)); return imbue(base->getSegment(id));
} }
...@@ -344,5 +394,9 @@ BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) { ...@@ -344,5 +394,9 @@ BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) {
return result; return result;
} }
void ImbuedBuilderArena::injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) {
return capInjector->injectCapInternal(pointer, kj::mv(cap));
}
} // namespace _ (private) } // namespace _ (private)
} // namespace capnp } // namespace capnp
...@@ -34,10 +34,13 @@ ...@@ -34,10 +34,13 @@
#include <kj/mutex.h> #include <kj/mutex.h>
#include "common.h" #include "common.h"
#include "message.h" #include "message.h"
#include "layout.h"
namespace capnp { namespace capnp {
class TypelessCapability; class CapExtractorBase;
class CapInjectorBase;
class ClientHook;
namespace _ { // private namespace _ { // private
...@@ -182,7 +185,7 @@ public: ...@@ -182,7 +185,7 @@ public:
// the VALIDATE_INPUT() macro which may throw an exception; if it return normally, the caller // the VALIDATE_INPUT() macro which may throw an exception; if it return normally, the caller
// will need to continue with default values. // will need to continue with default values.
virtual kj::Own<TypelessCapability> extractCap(const _::StructReader& capDescriptor); virtual kj::Own<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.
}; };
...@@ -196,6 +199,7 @@ public: ...@@ -196,6 +199,7 @@ public:
// implements Arena ------------------------------------------------ // implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override; SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override; void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
private: private:
MessageReader* message; MessageReader* message;
...@@ -210,7 +214,7 @@ private: ...@@ -210,7 +214,7 @@ private:
class ImbuedReaderArena final: public Arena { class ImbuedReaderArena final: public Arena {
public: public:
ImbuedReaderArena(Arena* base); ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor);
~ImbuedReaderArena() noexcept(false); ~ImbuedReaderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedReaderArena); KJ_DISALLOW_COPY(ImbuedReaderArena);
...@@ -219,9 +223,11 @@ public: ...@@ -219,9 +223,11 @@ public:
// implements Arena ------------------------------------------------ // implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override; SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override; void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
private: private:
Arena* base; Arena* base;
CapExtractorBase* capExtractor;
// Optimize for single-segment messages so that small messages are handled quickly. // Optimize for single-segment messages so that small messages are handled quickly.
SegmentReader segment0; SegmentReader segment0;
...@@ -248,7 +254,7 @@ public: ...@@ -248,7 +254,7 @@ public:
// the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific // the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
// segment first if there is one, then fall back to the arena. // segment first if there is one, then fall back to the arena.
virtual void injectCap(_::PointerBuilder pointer, kj::Own<TypelessCapability>&& cap) = 0; virtual void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) = 0;
// Add the capability to the message and initialize the given pointer as an interface pointer // Add the capability to the message and initialize the given pointer as an interface pointer
// pointing to this cap. // pointing to this cap.
}; };
...@@ -271,10 +277,12 @@ public: ...@@ -271,10 +277,12 @@ public:
// implements Arena ------------------------------------------------ // implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override; SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override; void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
// implements BuilderArena ----------------------------------------- // implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override; SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override; AllocateResult allocate(WordCount amount) override;
void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap);
private: private:
MessageBuilder* message; MessageBuilder* message;
...@@ -294,7 +302,7 @@ class ImbuedBuilderArena final: public BuilderArena { ...@@ -294,7 +302,7 @@ class ImbuedBuilderArena final: public BuilderArena {
// A BuilderArena imbued with the ability to inject capabilities. // A BuilderArena imbued with the ability to inject capabilities.
public: public:
ImbuedBuilderArena(BuilderArena* base); ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector);
~ImbuedBuilderArena() noexcept(false); ~ImbuedBuilderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedBuilderArena); KJ_DISALLOW_COPY(ImbuedBuilderArena);
...@@ -304,13 +312,16 @@ public: ...@@ -304,13 +312,16 @@ public:
// implements Arena ------------------------------------------------ // implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override; SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override; void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
// implements BuilderArena ----------------------------------------- // implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override; SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override; AllocateResult allocate(WordCount amount) override;
void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap);
private: private:
BuilderArena* base; BuilderArena* base;
CapInjectorBase* capInjector;
ImbuedSegmentBuilder segment0; ImbuedSegmentBuilder segment0;
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNP_PRIVATE
#include "capability-context.h"
#include "arena.h"
#include <kj/debug.h>
namespace capnp {
CapReaderContext::CapReaderContext(CapExtractorBase& extractor): extractor(&extractor) {}
CapReaderContext::~CapReaderContext() noexcept(false) {
if (extractor == nullptr) {
kj::dtor(arena());
}
}
ObjectPointer::Reader CapReaderContext::imbue(ObjectPointer::Reader base) {
KJ_REQUIRE(extractor != nullptr, "imbue() can only be called once.");
KJ_IF_MAYBE(oldArena, base.reader.getArena()) {
kj::ctor(arena(), oldArena, extractor);
} else {
KJ_FAIL_REQUIRE("Cannot imbue unchecked message.");
}
extractor = nullptr;
return ObjectPointer::Reader(base.reader.imbue(arena()));
}
CapBuilderContext::CapBuilderContext(CapInjectorBase& injector): injector(&injector) {}
CapBuilderContext::~CapBuilderContext() noexcept(false) {
if (injector == nullptr) {
kj::dtor(arena());
}
}
ObjectPointer::Builder CapBuilderContext::imbue(ObjectPointer::Builder base) {
KJ_REQUIRE(injector != nullptr, "imbue() can only be called once.");
kj::ctor(arena(), &base.builder.getArena(), injector);
injector = nullptr;
return ObjectPointer::Builder(base.builder.imbue(arena()));
}
} // namespace capnp
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Classes for imbuing message readers/builders with a capability context.
//
// These classes are for use by RPC implementations. Application code need not know about them.
//
// Normally, MessageReader and MessageBuilder do not support interface pointers because they
// are not RPC-aware and so have no idea how to convert between a serialized CapabilityDescriptor
// and a live capability. To fix this, a reader/builder object needs to be "imbued" with a
// capability context. This creates a new reader/builder which points at the same object but has
// the ability to deal with interface fields. Use `CapReaderContext` and `CapBuilderContext` to
// accomplish this.
#ifndef CAPNP_CAPABILITY_CONTEXT_H_
#define CAPNP_CAPABILITY_CONTEXT_H_
#include "layout.h"
#include "object.h"
#include <kj/mutex.h>
namespace capnp {
class ClientHook;
namespace _ { // private
class ImbuedReaderArena;
class ImbuedBuilderArena;
} // namespace _ (private)
class CapExtractorBase {
// Non-template base class for CapExtractor<T>.
private:
virtual kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) = 0;
friend class _::ImbuedReaderArena;
};
class CapInjectorBase {
// Non-template base class for CapInjector<T>.
private:
virtual void injectCapInternal(_::PointerBuilder builder, kj::Own<ClientHook>&& cap) = 0;
virtual kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) = 0;
friend class _::ImbuedBuilderArena;
};
template <typename CapDescriptor>
class CapExtractor: public CapExtractorBase {
// Callback used to read a capability from a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual kj::Own<ClientHook> extractCap(typename CapDescriptor::Reader descriptor) = 0;
// Given the descriptor read off the wire, construct a live capability.
private:
kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) override final {
return extractCap(typename CapDescriptor::Reader(capDescriptor));
}
};
template <typename CapDescriptor>
class CapInjector: public CapInjectorBase {
// Callback used to write a capability into a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual void injectCap(typename CapDescriptor::Builder descriptor, kj::Own<ClientHook>&& cap) = 0;
// Fill in the given descriptor so that it describes the given capability.
virtual kj::Own<ClientHook> getInjectedCap(typename CapDescriptor::Reader descriptor) = 0;
// Read back a cap that was previously injected with `injectCap`. This should return a new
// reference.
private:
void injectCapInternal(_::PointerBuilder builder, kj::Own<ClientHook>&& cap) override final {
injectCap(
typename CapDescriptor::Builder(builder.initCapDescriptor(_::structSize<CapDescriptor>())),
kj::mv(cap));
}
kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) {
return getInjectedCap(typename CapDescriptor::Reader(capDescriptor));
}
};
// -------------------------------------------------------------------
class CapReaderContext {
// Class which can "imbue" reader objects from some other message with a capability context,
// so that interface pointers found in the message can be extracted and called.
//
// `imbue()` can only be called once per context.
public:
CapReaderContext(CapExtractorBase& extractor);
~CapReaderContext() noexcept(false);
ObjectPointer::Reader imbue(ObjectPointer::Reader base);
private:
CapExtractorBase* extractor; // becomes null once arena is allocated
void* arenaSpace[12 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
_::ImbuedReaderArena& arena() { return *reinterpret_cast<_::ImbuedReaderArena*>(arenaSpace); }
friend class _::ImbuedReaderArena;
};
class CapBuilderContext {
// Class which can "imbue" reader objects from some other message with a capability context,
// so that interface pointers found in the message can be set to point at live capabilities.
//
// `imbue()` can only be called once per context.
public:
CapBuilderContext(CapInjectorBase& injector);
~CapBuilderContext() noexcept(false);
ObjectPointer::Builder imbue(ObjectPointer::Builder base);
private:
CapInjectorBase* injector; // becomes null once arena is allocated
void* arenaSpace[12 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
_::ImbuedBuilderArena& arena() { return *reinterpret_cast<_::ImbuedBuilderArena*>(arenaSpace); }
friend class _::ImbuedBuilderArena;
};
} // namespace capnp
#endif // CAPNP_CAPABILITY_CONTEXT_H_
...@@ -24,19 +24,18 @@ ...@@ -24,19 +24,18 @@
#include "capability.h" #include "capability.h"
namespace capnp { namespace capnp {
namespace _ { // private
CallResult::Pipeline CallResult::Pipeline::getPointerField(uint16_t pointerIndex) const { TypelessAnswer::Pipeline TypelessAnswer::Pipeline::getPointerField(
auto newOps = kj::heapArray<PipelineManager::Op>(ops.size() + 1); uint16_t pointerIndex) const {
auto newOps = kj::heapArray<PipelineOp>(ops.size() + 1);
for (auto i: kj::indices(ops)) { for (auto i: kj::indices(ops)) {
newOps[i] = ops[i]; newOps[i] = ops[i];
} }
auto& newOp = newOps[ops.size()]; auto& newOp = newOps[ops.size()];
newOp.type = PipelineManager::Op::GET_POINTER_FIELD; newOp.type = PipelineOp::GET_POINTER_FIELD;
newOp.pointerIndex = pointerIndex; newOp.pointerIndex = pointerIndex;
return Pipeline(call->addRef(), kj::mv(newOps)); return Pipeline(hook->addRef(), kj::mv(newOps));
} }
} // namespace _ (private)
} // namespace capnp } // namespace capnp
...@@ -29,221 +29,356 @@ ...@@ -29,221 +29,356 @@
namespace capnp { namespace capnp {
template <typename Answer>
class Response;
template <typename T> template <typename T>
class RemotePromise: public kj::Promise<kj::Own<ReaderFor<T>>>, public T::Pipeline { class RemotePromise: public kj::Promise<Response<T>>, public T::Pipeline {
// A Promise which supports pipelined calls. T is typically a struct type. // A Promise which supports pipelined calls. T is typically a struct type. T must declare
// an inner "mix-in" type "Pipeline" which implements pipelining; RemotePromise simply
// multiply-inherits that type along with Promise<Response<T>>. T::Pipeline must be movable,
// but does not need to be copyable (i.e. just like Promise<T>).
//
// The promise is for an owned pointer so that the RPC system can allocate the MessageReader
// itself.
public: public:
inline RemotePromise(kj::Promise<kj::Own<ReaderFor<T>>>&& promise, inline RemotePromise(kj::Promise<Response<T>>&& promise, typename T::Pipeline&& pipeline)
typename T::Pipeline&& pipeline) : kj::Promise<Response<T>>(kj::mv(promise)),
: kj::Promise<kj::Own<ReaderFor<T>>>(kj::mv(promise)),
T::Pipeline(kj::mv(pipeline)) {} T::Pipeline(kj::mv(pipeline)) {}
inline RemotePromise(decltype(nullptr)) inline RemotePromise(decltype(nullptr))
: kj::Promise<kj::Own<ReaderFor<T>>>(nullptr) {} : kj::Promise<Response<T>>(nullptr) {}
KJ_DISALLOW_COPY(RemotePromise); KJ_DISALLOW_COPY(RemotePromise);
RemotePromise(RemotePromise&& other) = default; RemotePromise(RemotePromise&& other) = default;
RemotePromise& operator=(RemotePromise&& other) = default; RemotePromise& operator=(RemotePromise&& other) = default;
}; };
struct Capability {
// A capability without type-safe methods. Typed capability clients wrap `Client` and typed
// capability servers subclass `Server` to dispatch to the regular, typed methods.
class Client;
class Server;
};
// ======================================================================================= // =======================================================================================
class Call; class RequestHook;
struct CallResult; class ResponseHook;
class PipelineHook;
class ClientHook;
class TypelessCapability { template <typename Params, typename Answer>
// This is an internal type used to represent a live capability (or a promise for a capability). class Request: public Params::Builder {
// This class should not be used directly by applications; it is intended to be used by the // A call that hasn't been sent yet. This class extends a Builder for the call's "Params"
// generated code wrappers. // structure with a method send() that actually sends it.
//
// Given a Cap'n Proto method `foo(a :A, b :B): C`, the generated client interface will have
// a method `Request<FooParams, C> startFoo()` (as well as a convenience method
// `RemotePromise<C> foo(A::Reader a, B::Reader b)`).
public: public:
virtual kj::Own<Call> newCall(uint64_t interfaceId, uint16_t methodId) const = 0; inline Request(typename Params::Builder builder, kj::Own<RequestHook>&& hook)
// Begin a new call to a method of this capability. : Params::Builder(builder), hook(kj::mv(hook)) {}
virtual kj::Own<TypelessCapability> addRef() const = 0; RemotePromise<Answer> send();
// Return a new reference-counted pointer to the same capability. (Reference counting can be // Send the call and return a promise for the answer.
// implemented using a special Disposer, so that the returned pointer actually has the same
// identity as the original.)
virtual kj::Promise<void> whenResolved() const = 0; private:
kj::Own<RequestHook> hook;
};
template <typename Answer>
class Response: public Answer::Reader {
// A completed call. This class extends a Reader for the call's answer structure. The Response
// is move-only -- once it goes out-of-scope, the underlying message will be freed.
public:
inline Response(typename Answer::Reader reader, kj::Own<ResponseHook>&& hook)
: Answer::Reader(reader), hook(kj::mv(hook)) {}
private:
kj::Own<ResponseHook> hook;
template <typename, typename>
friend class Request;
};
class Capability::Client {
// Base type for capability clients.
public:
explicit Client(kj::Own<ClientHook>&& hook);
Client(const Client& other);
Client& operator=(const Client& other);
// Copies by reference counting. Warning: Refcounting is slow due to atomic ops. Try to only
// use move instead.
Client(Client&&) = default;
Client& operator=(Client&&) = default;
// Move is fast.
kj::Promise<void> whenResolved() const;
// If the capability is actually only a promise, the returned promise resolves once the // If the capability is actually only a promise, the returned promise resolves once the
// capability itself has resolved to its final destination (or propagates the exception if // capability itself has resolved to its final destination (or propagates the exception if
// the capability promise is rejected). This is mainly useful for error-checking in the case // the capability promise is rejected). This is mainly useful for error-checking in the case
// where no calls are being made. There is no reason to wait for this before making calls; if // where no calls are being made. There is no reason to wait for this before making calls; if
// the capability does not resolve, the call results will propagate the error. // the capability does not resolve, the call results will propagate the error.
// TODO(soon): method implementing Join // TODO(soon): method(s) for Join
private:
kj::Own<ClientHook> hook;
}; };
// ======================================================================================= // =======================================================================================
// Local capabilities
class CallContextHook;
template <typename T>
class CallContext: public kj::DisallowConstCopy {
// Wrapper around TypelessCallContext with a specific return type.
class Call {
public: public:
virtual ObjectPointer::Builder getRequest() = 0; explicit CallContext(kj::Own<CallContextHook> hook);
// Get the request object for this call, to be filled in before sending.
typename T::Builder getAnswer();
typename T::Builder initAnswer();
typename T::Builder initAnswer(uint size);
void setAnswer(typename T::Reader value);
void adoptAnswer(Orphan<T>&& value);
Orphanage getAnswerOrphanage();
// Manipulate the answer (return value) payload. The "Return" message (part of the RPC protocol)
// will typically be allocated the first time one of these is called. Some RPC systems may
// allocate these messages in a limited space (such as a shared memory segment), therefore the
// application should delay calling these as long as is convenient to do so (but don't delay
// if doing so would require extra copies later).
void allowAsyncCancellation(bool allow = true);
// Indicate that it is OK for the RPC system to discard its Promise for this call's result if
// the caller cancels the call, thereby transitively canceling any asynchronous operations the
// call implementation was performing. This is not done by default because it could represent a
// security risk: applications must be carefully written to ensure that they do not end up in
// a bad state if an operation is canceled at an arbitrary point. However, for long-running
// method calls that hold significant resources, prompt cancellation is often useful.
//
// You can also switch back to disallowing cancellation by passing `false` as the argument.
//
// Keep in mind that asynchronous cancellation cannot occur while the method is synchronously
// executing on a local thread. The method must perform an asynchronous operation or call
// `EventLoop::current().runLater()` to yield control.
bool isCanceled();
// As an alternative to `allowAsyncCancellation()`, a server can call this to check for
// cancellation.
//
// Keep in mind that if the method is blocking the event loop, the cancel message won't be
// received, so it is necessary to use `EventLoop::current().runLater()` occasionally.
virtual RemotePromise<CallResult> send() = 0; private:
// Send the call and return a promise for the result. kj::Own<CallContextHook> hook;
}; };
class CallRunner { class Capability::Server {
// Implements pipelined requests for a particular outstanding call. // Objects implementing a Cap'n Proto interface must subclass this. Typically, such objects
// will instead subclass a typed Server interface which will take care of implementing
// dispatchCall().
public: public:
virtual kj::Own<CallRunner> addRef() const = 0; virtual kj::Promise<void> dispatchCall(uint64_t interfaceId, uint16_t methodId,
// Increment this object's reference count. kj::Own<ObjectPointer::Reader> params,
CallContext<ObjectPointer> context) = 0;
// Call the given method. `params` is the input struct, and should be released as soon as it
// is no longer needed. `context` may be used to allocate the output struct and deal with
// cancellation.
// TODO(soon): Method which can optionally be overridden to implement Join when the object is
// a proxy.
};
Capability::Client makeLocalClient(kj::Own<Capability::Server>&& server);
// Make a client capability that wraps the given server capability.
// =======================================================================================
struct PipelineOp { struct PipelineOp {
enum Type { enum Type {
GET_POINTER_FIELD GET_POINTER_FIELD
};
Type type; // There may be other types in the future...
union {
uint16_t pointerIndex;
};
}; };
virtual kj::Own<TypelessCapability> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) const = 0; Type type;
// Extract a promised Capability from the answer. union {
uint16_t pointerIndex; // for GET_POINTER_FIELD
};
}; };
struct CallResult { struct TypelessAnswer {
// Result of a call. Designed to be used as RemotePromise<CallResult>. // Result of a call, before it has been type-wrapped. Designed to be used as
// RemotePromise<CallResult>.
typedef ObjectPointer::Reader Reader; typedef ObjectPointer::Reader Reader;
// So RemotePromise<CallResult> resolves to Own<ObjectPointer::Reader>. // So RemotePromise<CallResult> resolves to Own<ObjectPointer::Reader>.
class Pipeline { class Pipeline {
public: public:
inline explicit Pipeline(kj::Own<CallRunner>&& runner): runner(kj::mv(runner)) {} inline explicit Pipeline(kj::Own<PipelineHook>&& hook): hook(kj::mv(hook)) {}
Pipeline getPointerField(uint16_t pointerIndex) const; Pipeline getPointerField(uint16_t pointerIndex) const;
// Return a new Promise representing a sub-object of the result. `pointerIndex` is the index // 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). // 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. // TODO(kenton): On GCC 4.8 / Clang 3.3, use rvalue qualifiers to avoid the need for copies.
// Make `ops` into a Vector so that it can be extended without allocation in this case. // Also make `ops` into a Vector to optimize this.
inline kj::Own<TypelessCapability> asCap() const { return runner->getPipelinedCap(ops); } Capability::Client asCap() const;
// Expect that the result is a capability and construct a pipelined version of it now. // Expect that the result is a capability and construct a pipelined version of it now.
private: private:
kj::Own<CallRunner> runner; kj::Own<PipelineHook> hook;
kj::Array<CallRunner::PipelineOp> ops; kj::Array<PipelineOp> ops;
inline Pipeline(kj::Own<CallRunner>&& runner, kj::Array<CallRunner::PipelineOp>&& ops) inline Pipeline(kj::Own<PipelineHook>&& hook, kj::Array<PipelineOp>&& ops)
: runner(kj::mv(runner)), ops(kj::mv(ops)) {} : hook(kj::mv(hook)), ops(kj::mv(ops)) {}
}; };
}; };
// ======================================================================================= // =======================================================================================
// Classes for imbuing message readers/builders with a capability context. // 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.
// These classes are for use by RPC implementations. Application code need not know about them.
//
// TODO(kenton): Move these to a separate header.
//
// Normally, MessageReader and MessageBuilder do not support interface pointers because they
// are not RPC-aware and so have no idea how to convert between a serialized CapabilityDescriptor
// and a live capability. To fix this, a reader/builder object needs to be "imbued" with a
// capability context. This creates a new reader/builder which points at the same object but has
// the ability to deal with interface fields.
namespace _ { // private
class ImbuedReaderArena;
class ImbuedBuilderArena;
} // namespace _ (private)
class CapExtractorBase {
// Non-template base class for CapExtractor<T>.
private: class RequestHook {
virtual kj::Own<TypelessCapability> extractCapInternal(const _::StructReader& capDescriptor) = 0; // Hook interface implemented by RPC system representing a request being built.
friend class _::ImbuedReaderArena;
};
class CapInjectorBase { public:
// Non-template base class for CapInjector<T>. virtual ObjectPointer::Builder getRequest() = 0;
// Get the request object for this call, to be filled in before sending.
private: virtual RemotePromise<TypelessAnswer> send() = 0;
virtual void injectCapInternal(_::PointerBuilder builder, kj::Own<TypelessCapability>&& cap) = 0; // Send the call and return a promise for the result.
friend class _::ImbuedBuilderArena;
}; };
template <typename CapDescriptor> class ResponseHook {
class CapExtractor: public CapExtractorBase { // Hook interface implemented by RPC system representing a response.
// Callback used to read a capability from a message, implemented by the RPC system. //
// `CapDescriptor` is the struct type which the RPC implementation uses to represent // At present this class has no methods. It exists only for garbage collection -- when the
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.) // ResponseHook is destroyed, the answer can be freed.
public: public:
virtual kj::Own<TypelessCapability> extractCap(typename CapDescriptor::Reader descriptor) = 0;
// Given the descriptor read off the wire, construct a live capability.
private:
kj::Own<TypelessCapability> extractCapInternal(
const _::StructReader& capDescriptor) override final {
return extractCap(typename CapDescriptor::Reader(capDescriptor));
}
}; };
template <typename CapDescriptor> class PipelineHook {
class CapInjector: public CapInjectorBase { // Represents a currently-running call, and implements pipelined requests on its result.
// Callback used to write a capability into a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public: public:
virtual void injectCap(typename CapDescriptor::Builder descriptor, virtual kj::Own<PipelineHook> addRef() const = 0;
kj::Own<TypelessCapability>&& cap) = 0; // Increment this object's reference count.
// Fill in the given descriptor so that it describes the given capability.
private: virtual kj::Own<ClientHook> getPipelinedCap(kj::ArrayPtr<const PipelineOp> ops) const = 0;
void injectCapInternal(_::PointerBuilder builder, // Extract a promised Capability from the answer.
kj::Own<TypelessCapability>&& cap) override final {
injectCap(
typename CapDescriptor::Builder(builder.initCapDescriptor(_::structSize<CapDescriptor>())),
kj::mv(cap));
}
}; };
class CapReaderContext { class ClientHook {
// Class which can "imbue" reader objects from some other message with a capability context,
// so that interface pointers found in the message can be extracted and called.
public: public:
CapReaderContext(Orphanage arena, CapExtractorBase& extractor); virtual Request<ObjectPointer, TypelessAnswer> newCall(
~CapReaderContext() noexcept(false); uint64_t interfaceId, uint16_t methodId) const = 0;
virtual kj::Promise<void> whenResolved() const = 0;
ObjectPointer::Reader imbue(ObjectPointer::Reader base);
private:
void* arenaSpace[15 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
virtual kj::Own<TypelessCapability> extractCapInternal(const _::StructReader& capDescriptor) = 0; virtual kj::Own<ClientHook> addRef() const = 0;
// Return a new reference to the same capability.
friend class _::ImbuedReaderArena; virtual void* getBrand() const = 0;
// Returns a void* that identifies who made this client. This can be used by an RPC adapter to
// discover when a capability it needs to marshal is one that it created in the first place, and
// therefore it can transfer the capability without proxying.
}; };
class CapBuilderContext { class CallContextHook {
// Class which can "imbue" reader objects from some other message with a capability context, // Hook interface implemented by RPC system to manage a call on the server side. See
// so that interface pointers found in the message can be set to point at live capabilities. // CallContext<T>.
public: public:
CapBuilderContext(Orphanage arena, CapInjectorBase& injector); virtual ObjectPointer::Builder getAnswer() = 0;
~CapBuilderContext() noexcept(false); virtual void allowAsyncCancellation(bool allow) = 0;
virtual bool isCanceled() = 0;
ObjectPointer::Builder imbue(ObjectPointer::Builder base); };
private:
void* arenaSpace[15 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
virtual void injectCapInternal(_::PointerBuilder builder, kj::Own<TypelessCapability>&& cap) = 0; // =======================================================================================
// Inline implementation details
template <typename Params, typename Answer>
RemotePromise<Answer> Request<Params, Answer>::send() {
auto typelessPromise = hook->send();
// Convert the Promise to return the correct response type.
// Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the
// Pipeline part of the RemotePromise.
auto typedPromise = kj::implicitCast<kj::Promise<Response<TypelessAnswer>>&>(typelessPromise)
.then([](Response<TypelessAnswer>&& response) -> Response<Answer> {
return Response<Answer>(response.getAs<Answer>(), kj::mv(response.hook));
});
// Wrap the typeless pipeline in a typed wrapper.
typename Answer::Pipeline typedPipeline(
kj::mv(kj::implicitCast<TypelessAnswer::Pipeline&>(typelessPromise)));
return RemotePromise<Answer>(kj::mv(typedPromise), kj::mv(typedPipeline));
}
inline Capability::Client TypelessAnswer::Pipeline::asCap() const {
return Capability::Client(hook->getPipelinedCap(ops));
}
inline Capability::Client::Client(kj::Own<ClientHook>&& hook): hook(kj::mv(hook)) {}
inline Capability::Client::Client(const Client& other): hook(other.hook->addRef()) {}
inline Capability::Client& Capability::Client::operator=(const Client& other) {
hook = other.hook->addRef();
return *this;
}
inline kj::Promise<void> Capability::Client::whenResolved() const {
return hook->whenResolved();
}
friend class _::ImbuedBuilderArena; template <typename T>
}; inline CallContext<T>::CallContext(kj::Own<CallContextHook> hook): hook(kj::mv(hook)) {}
template <typename T>
inline typename T::Builder CallContext<T>::getAnswer() {
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
return hook->getAnswer().template getAs<T>();
}
template <typename T>
inline typename T::Builder CallContext<T>::initAnswer() {
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
return hook->getAnswer().template initAs<T>();
}
template <typename T>
inline typename T::Builder CallContext<T>::initAnswer(uint size) {
// `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401
return hook->getAnswer().template initAs<T>(size);
}
template <typename T>
inline void CallContext<T>::setAnswer(typename T::Reader value) {
hook->getAnswer().set(value);
}
template <typename T>
inline void CallContext<T>::adoptAnswer(Orphan<T>&& value) {
hook->getAnswer().adopt(kj::mv(value));
}
template <typename T>
inline Orphanage CallContext<T>::getAnswerOrphanage() {
return Orphanage::getForMessageContaining(hook->getAnswer());
}
template <typename T>
inline void CallContext<T>::allowAsyncCancellation(bool allow) {
hook->allowAsyncCancellation(allow);
}
template <typename T>
inline bool CallContext<T>::isCanceled() {
return hook->isCanceled();
}
} // namespace capnp } // namespace capnp
......
...@@ -2104,6 +2104,14 @@ PointerReader PointerBuilder::asReader() const { ...@@ -2104,6 +2104,14 @@ PointerReader PointerBuilder::asReader() const {
return PointerReader(segment, pointer, std::numeric_limits<int>::max()); return PointerReader(segment, pointer, std::numeric_limits<int>::max());
} }
BuilderArena& PointerBuilder::getArena() const {
return *segment->getArena();
}
PointerBuilder PointerBuilder::imbue(ImbuedBuilderArena& newArena) const {
return PointerBuilder(newArena.imbue(segment), pointer);
}
// ======================================================================================= // =======================================================================================
// PointerReader // PointerReader
...@@ -2139,6 +2147,14 @@ bool PointerReader::isNull() const { ...@@ -2139,6 +2147,14 @@ bool PointerReader::isNull() const {
return pointer == nullptr || pointer->isNull(); return pointer == nullptr || pointer->isNull();
} }
kj::Maybe<Arena&> PointerReader::getArena() const {
return segment == nullptr ? nullptr : segment->getArena();
}
PointerReader PointerReader::imbue(ImbuedReaderArena& newArena) const {
return PointerReader(newArena.imbue(segment), pointer, nestingLimit);
}
// ======================================================================================= // =======================================================================================
// StructBuilder // StructBuilder
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
namespace capnp { namespace capnp {
class TypelessCapability; class ClientHook;
namespace _ { // private namespace _ { // private
...@@ -54,7 +54,10 @@ struct WirePointer; ...@@ -54,7 +54,10 @@ struct WirePointer;
struct WireHelpers; struct WireHelpers;
class SegmentReader; class SegmentReader;
class SegmentBuilder; class SegmentBuilder;
class Arena;
class BuilderArena; class BuilderArena;
class ImbuedReaderArena;
class ImbuedBuilderArena;
// ============================================================================= // =============================================================================
...@@ -284,7 +287,7 @@ public: ...@@ -284,7 +287,7 @@ public:
ListBuilder getList(FieldSize elementSize, const word* defaultValzue); ListBuilder getList(FieldSize elementSize, const word* defaultValzue);
ListBuilder getStructList(StructSize elementSize, const word* defaultValue); ListBuilder getStructList(StructSize elementSize, const word* defaultValue);
template <typename T> typename T::Builder getBlob(const void* defaultValue,ByteCount defaultSize); template <typename T> typename T::Builder getBlob(const void* defaultValue,ByteCount defaultSize);
kj::Own<TypelessCapability> getCapability(); kj::Own<ClientHook> getCapability();
// Get methods: Get the value. If it is null, initialize it to a copy of the default value. // Get methods: Get the value. If it is null, initialize it to a copy of the default value.
// The default value is encoded as an "unchecked message" for structs, lists, and objects, or a // The default value is encoded as an "unchecked message" for structs, lists, and objects, or a
// simple byte array for blobs. // simple byte array for blobs.
...@@ -300,7 +303,7 @@ public: ...@@ -300,7 +303,7 @@ public:
void setStruct(const StructReader& value); void setStruct(const StructReader& value);
void setList(const ListReader& value); void setList(const ListReader& value);
template <typename T> void setBlob(typename T::Reader value); template <typename T> void setBlob(typename T::Reader value);
void setCapability(kj::Own<TypelessCapability>&& cap); void setCapability(kj::Own<ClientHook>&& cap);
// Set methods: Initialize the pointer to a newly-allocated copy of the given value, discarding // Set methods: Initialize the pointer to a newly-allocated copy of the given value, discarding
// the existing object. // the existing object.
...@@ -321,6 +324,12 @@ public: ...@@ -321,6 +324,12 @@ public:
PointerReader asReader() const; PointerReader asReader() const;
BuilderArena& getArena() const;
// Get the arena containing this pointer.
PointerBuilder imbue(ImbuedBuilderArena& newArena) const;
// Imbue the pointer with a capability context, returning the imbued pointer.
private: private:
SegmentBuilder* segment; // Memory segment in which the pointer resides. SegmentBuilder* segment; // Memory segment in which the pointer resides.
WirePointer* pointer; // Pointer to the pointer. WirePointer* pointer; // Pointer to the pointer.
...@@ -342,7 +351,7 @@ public: ...@@ -342,7 +351,7 @@ public:
ListReader getList(FieldSize expectedElementSize, const word* defaultValue) const; ListReader getList(FieldSize expectedElementSize, const word* defaultValue) const;
template <typename T> template <typename T>
typename T::Reader getBlob(const void* defaultValue, ByteCount defaultSize) const; typename T::Reader getBlob(const void* defaultValue, ByteCount defaultSize) const;
kj::Own<TypelessCapability> getCapability(); kj::Own<ClientHook> getCapability();
// Get methods: Get the value. If it is null, return the default value instead. // Get methods: Get the value. If it is null, return the default value instead.
// The default value is encoded as an "unchecked message" for structs, lists, and objects, or a // The default value is encoded as an "unchecked message" for structs, lists, and objects, or a
// simple byte array for blobs. // simple byte array for blobs.
...@@ -352,6 +361,12 @@ public: ...@@ -352,6 +361,12 @@ public:
// word* can actually be passed to readUnchecked() to read the designated sub-object later. If // word* can actually be passed to readUnchecked() to read the designated sub-object later. If
// this isn't an unchecked message, throws an exception. // this isn't an unchecked message, throws an exception.
kj::Maybe<Arena&> getArena() const;
// Get the arena containing this pointer.
PointerReader imbue(ImbuedReaderArena& newArena) const;
// Imbue the pointer with a capability context, returning the imbued pointer.
private: private:
SegmentReader* segment; // Memory segment in which the pointer resides. SegmentReader* segment; // Memory segment in which the pointer resides.
const WirePointer* pointer; // Pointer to the pointer. null = treat as null pointer. const WirePointer* pointer; // Pointer to the pointer. null = treat as null pointer.
......
...@@ -63,6 +63,7 @@ struct ObjectPointer { ...@@ -63,6 +63,7 @@ struct ObjectPointer {
_::PointerReader reader; _::PointerReader reader;
friend struct ObjectPointer; friend struct ObjectPointer;
friend class Orphanage; friend class Orphanage;
friend class CapReaderContext;
}; };
class Builder { class Builder {
...@@ -143,6 +144,7 @@ struct ObjectPointer { ...@@ -143,6 +144,7 @@ struct ObjectPointer {
private: private:
_::PointerBuilder builder; _::PointerBuilder builder;
friend class CapBuilderContext;
}; };
}; };
...@@ -281,6 +283,38 @@ inline Orphan<T> Orphan<ObjectPointer>::releaseAs() { ...@@ -281,6 +283,38 @@ inline Orphan<T> Orphan<ObjectPointer>::releaseAs() {
return Orphan<T>(kj::mv(builder)); return Orphan<T>(kj::mv(builder));
} }
// Using ObjectPointer as the template type should work...
template <>
inline typename ObjectPointer::Reader ObjectPointer::Reader::getAs<ObjectPointer>() {
return *this;
}
template <>
inline typename ObjectPointer::Builder ObjectPointer::Builder::getAs<ObjectPointer>() {
return *this;
}
template <>
inline typename ObjectPointer::Builder ObjectPointer::Builder::initAs<ObjectPointer>() {
clear();
return *this;
}
template <>
inline void ObjectPointer::Builder::setAs<ObjectPointer>(ObjectPointer::Reader value) {
return builder.copyFrom(value.reader);
}
template <>
inline void ObjectPointer::Builder::adopt<ObjectPointer>(Orphan<ObjectPointer>&& orphan) {
builder.adopt(kj::mv(orphan.builder));
}
template <>
inline Orphan<ObjectPointer> ObjectPointer::Builder::disownAs<ObjectPointer>() {
return Orphan<ObjectPointer>(builder.disown());
}
template <>
inline Orphan<ObjectPointer> Orphan<ObjectPointer>::releaseAs() {
return kj::mv(*this);
}
} // namespace capnp } // namespace capnp
#endif // CAPNP_OBJECT_H_ #endif // CAPNP_OBJECT_H_
...@@ -950,7 +950,7 @@ using JoinAnswer = Object; ...@@ -950,7 +950,7 @@ using JoinAnswer = Object;
# #
# # Level 4 features ----------------------------------------------- # # Level 4 features -----------------------------------------------
# #
# newJoiner(count :UInt32): NewJoinerResponse; # newJoiner(count :UInt32) :NewJoinerResponse;
# # Prepare a new Join operation, which will eventually lead to forming a new direct connection # # Prepare a new Join operation, which will eventually lead to forming a new direct connection
# # to the host of the joined capability. `count` is the number of capabilities to join. # # to the host of the joined capability. `count` is the number of capabilities to join.
# #
...@@ -973,7 +973,7 @@ using JoinAnswer = Object; ...@@ -973,7 +973,7 @@ using JoinAnswer = Object;
# # message on it with the specified `ProvisionId` in order to receive the final capability. # # message on it with the specified `ProvisionId` in order to receive the final capability.
# } # }
# #
# acceptConnectionFromJoiner(parts: List(JoinKeyPart), paths :List(VatPath)) # acceptConnectionFromJoiner(parts :List(JoinKeyPart), paths :List(VatPath))
# :ConnectionAndProvisionId; # :ConnectionAndProvisionId;
# # Called on a joined capability's host to receive the connection from the joiner, once all # # Called on a joined capability's host to receive the connection from the joiner, once all
# # key parts have arrived. The caller should expect to receive an `Accept` message over the # # key parts have arrived. The caller should expect to receive an `Accept` message over the
...@@ -1002,17 +1002,17 @@ using JoinAnswer = Object; ...@@ -1002,17 +1002,17 @@ using JoinAnswer = Object;
# sendToTarget :RecipientId; # sendToTarget :RecipientId;
# } # }
# #
# connectToIntroduced(capId: ThirdPartyCapId) :ConnectionAndProvisionId; # connectToIntroduced(capId :ThirdPartyCapId) :ConnectionAndProvisionId;
# # Given a ThirdPartyCapId received over this connection, connect to the third party. The # # Given a ThirdPartyCapId received over this connection, connect to the third party. The
# # caller should then send an `Accept` message over the new connection. # # caller should then send an `Accept` message over the new connection.
# #
# acceptIntroducedConnection(recipientId: RecipientId): Connection # acceptIntroducedConnection(recipientId :RecipientId) :Connection;
# # Given a RecipientId received in a `Provide` message on this `Connection`, wait for the # # Given a RecipientId received in a `Provide` message on this `Connection`, wait for the
# # recipient to connect, and return the connection formed. Usually, the first message received # # recipient to connect, and return the connection formed. Usually, the first message received
# # on the new connection will be an `Accept` message. # # on the new connection will be an `Accept` message.
# } # }
# #
# sturct ConnectionAndProvisionId { # struct ConnectionAndProvisionId {
# # **(level 3)** # # **(level 3)**
# #
# connection :Connection; # connection :Connection;
......
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