cannam@49: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@49: // Licensed under the MIT License: cannam@49: // cannam@49: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@49: // of this software and associated documentation files (the "Software"), to deal cannam@49: // in the Software without restriction, including without limitation the rights cannam@49: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@49: // copies of the Software, and to permit persons to whom the Software is cannam@49: // furnished to do so, subject to the following conditions: cannam@49: // cannam@49: // The above copyright notice and this permission notice shall be included in cannam@49: // all copies or substantial portions of the Software. cannam@49: // cannam@49: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@49: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@49: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@49: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@49: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@49: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@49: // THE SOFTWARE. cannam@49: cannam@49: #ifndef CAPNP_CAPABILITY_H_ cannam@49: #define CAPNP_CAPABILITY_H_ cannam@49: cannam@49: #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) cannam@49: #pragma GCC system_header cannam@49: #endif cannam@49: cannam@49: #if CAPNP_LITE cannam@49: #error "RPC APIs, including this header, are not available in lite mode." cannam@49: #endif cannam@49: cannam@49: #include cannam@49: #include cannam@49: #include "any.h" cannam@49: #include "pointer-helpers.h" cannam@49: cannam@49: namespace capnp { cannam@49: cannam@49: template cannam@49: class Response; cannam@49: cannam@49: template cannam@49: class RemotePromise: public kj::Promise>, public T::Pipeline { cannam@49: // A Promise which supports pipelined calls. T is typically a struct type. T must declare cannam@49: // an inner "mix-in" type "Pipeline" which implements pipelining; RemotePromise simply cannam@49: // multiply-inherits that type along with Promise>. T::Pipeline must be movable, cannam@49: // but does not need to be copyable (i.e. just like Promise). cannam@49: // cannam@49: // The promise is for an owned pointer so that the RPC system can allocate the MessageReader cannam@49: // itself. cannam@49: cannam@49: public: cannam@49: inline RemotePromise(kj::Promise>&& promise, typename T::Pipeline&& pipeline) cannam@49: : kj::Promise>(kj::mv(promise)), cannam@49: T::Pipeline(kj::mv(pipeline)) {} cannam@49: inline RemotePromise(decltype(nullptr)) cannam@49: : kj::Promise>(nullptr), cannam@49: T::Pipeline(nullptr) {} cannam@49: KJ_DISALLOW_COPY(RemotePromise); cannam@49: RemotePromise(RemotePromise&& other) = default; cannam@49: RemotePromise& operator=(RemotePromise&& other) = default; cannam@49: }; cannam@49: cannam@49: class LocalClient; cannam@49: namespace _ { // private cannam@49: struct RawSchema; cannam@49: struct RawBrandedSchema; cannam@49: extern const RawSchema NULL_INTERFACE_SCHEMA; // defined in schema.c++ cannam@49: class CapabilityServerSetBase; cannam@49: } // namespace _ (private) cannam@49: cannam@49: struct Capability { cannam@49: // A capability without type-safe methods. Typed capability clients wrap `Client` and typed cannam@49: // capability servers subclass `Server` to dispatch to the regular, typed methods. cannam@49: cannam@49: class Client; cannam@49: class Server; cannam@49: cannam@49: struct _capnpPrivate { cannam@49: struct IsInterface; cannam@49: static constexpr uint64_t typeId = 0x3; cannam@49: static constexpr Kind kind = Kind::INTERFACE; cannam@49: static constexpr _::RawSchema const* schema = &_::NULL_INTERFACE_SCHEMA; cannam@49: cannam@49: static const _::RawBrandedSchema* const brand; cannam@49: // Can't quite declare this one inline without including generated-header-support.h. Avoiding cannam@49: // for now by declaring out-of-line. cannam@49: // TODO(cleanup): Split RawSchema stuff into its own header that can be included here, or cannam@49: // something. cannam@49: }; cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: // Capability clients cannam@49: cannam@49: class RequestHook; cannam@49: class ResponseHook; cannam@49: class PipelineHook; cannam@49: class ClientHook; cannam@49: cannam@49: template cannam@49: class Request: public Params::Builder { cannam@49: // A call that hasn't been sent yet. This class extends a Builder for the call's "Params" cannam@49: // structure with a method send() that actually sends it. cannam@49: // cannam@49: // Given a Cap'n Proto method `foo(a :A, b :B): C`, the generated client interface will have cannam@49: // a method `Request fooRequest()` (as well as a convenience method cannam@49: // `RemotePromise foo(A::Reader a, B::Reader b)`). cannam@49: cannam@49: public: cannam@49: inline Request(typename Params::Builder builder, kj::Own&& hook) cannam@49: : Params::Builder(builder), hook(kj::mv(hook)) {} cannam@49: inline Request(decltype(nullptr)): Params::Builder(nullptr) {} cannam@49: cannam@49: RemotePromise send() KJ_WARN_UNUSED_RESULT; cannam@49: // Send the call and return a promise for the results. cannam@49: cannam@49: private: cannam@49: kj::Own hook; cannam@49: cannam@49: friend class Capability::Client; cannam@49: friend struct DynamicCapability; cannam@49: template cannam@49: friend class CallContext; cannam@49: friend class RequestHook; cannam@49: }; cannam@49: cannam@49: template cannam@49: class Response: public Results::Reader { cannam@49: // A completed call. This class extends a Reader for the call's answer structure. The Response cannam@49: // is move-only -- once it goes out-of-scope, the underlying message will be freed. cannam@49: cannam@49: public: cannam@49: inline Response(typename Results::Reader reader, kj::Own&& hook) cannam@49: : Results::Reader(reader), hook(kj::mv(hook)) {} cannam@49: cannam@49: private: cannam@49: kj::Own hook; cannam@49: cannam@49: template cannam@49: friend class Request; cannam@49: friend class ResponseHook; cannam@49: }; cannam@49: cannam@49: class Capability::Client { cannam@49: // Base type for capability clients. cannam@49: cannam@49: public: cannam@49: typedef Capability Reads; cannam@49: typedef Capability Calls; cannam@49: cannam@49: Client(decltype(nullptr)); cannam@49: // If you need to declare a Client before you have anything to assign to it (perhaps because cannam@49: // the assignment is going to occur in an if/else scope), you can start by initializing it to cannam@49: // `nullptr`. The resulting client is not meant to be called and throws exceptions from all cannam@49: // methods. cannam@49: cannam@49: template ()>> cannam@49: Client(kj::Own&& server); cannam@49: // Make a client capability that wraps the given server capability. The server's methods will cannam@49: // only be executed in the given EventLoop, regardless of what thread calls the client's methods. cannam@49: cannam@49: template ()>> cannam@49: Client(kj::Promise&& promise); cannam@49: // Make a client from a promise for a future client. The resulting client queues calls until the cannam@49: // promise resolves. cannam@49: cannam@49: Client(kj::Exception&& exception); cannam@49: // Make a broken client that throws the given exception from all calls. cannam@49: cannam@49: Client(Client& other); cannam@49: Client& operator=(Client& other); cannam@49: // Copies by reference counting. Warning: This refcounting is not thread-safe. All copies of cannam@49: // the client must remain in one thread. cannam@49: cannam@49: Client(Client&&) = default; cannam@49: Client& operator=(Client&&) = default; cannam@49: // Move constructor avoids reference counting. cannam@49: cannam@49: explicit Client(kj::Own&& hook); cannam@49: // For use by the RPC implementation: Wrap a ClientHook. cannam@49: cannam@49: template cannam@49: typename T::Client castAs(); cannam@49: // Reinterpret the capability as implementing the given interface. Note that no error will occur cannam@49: // here if the capability does not actually implement this interface, but later method calls will cannam@49: // fail. It's up to the application to decide how indicate that additional interfaces are cannam@49: // supported. cannam@49: // cannam@49: // TODO(perf): GCC 4.8 / Clang 3.3: rvalue-qualified version for better performance. cannam@49: cannam@49: template cannam@49: typename T::Client castAs(InterfaceSchema schema); cannam@49: // Dynamic version. `T` must be `DynamicCapability`, and you must `#include `. cannam@49: cannam@49: kj::Promise whenResolved(); cannam@49: // If the capability is actually only a promise, the returned promise resolves once the cannam@49: // capability itself has resolved to its final destination (or propagates the exception if cannam@49: // the capability promise is rejected). This is mainly useful for error-checking in the case cannam@49: // where no calls are being made. There is no reason to wait for this before making calls; if cannam@49: // the capability does not resolve, the call results will propagate the error. cannam@49: cannam@49: Request typelessRequest( cannam@49: uint64_t interfaceId, uint16_t methodId, cannam@49: kj::Maybe sizeHint); cannam@49: // Make a request without knowing the types of the params or results. You specify the type ID cannam@49: // and method number manually. cannam@49: cannam@49: // TODO(someday): method(s) for Join cannam@49: cannam@49: protected: cannam@49: Client() = default; cannam@49: cannam@49: template cannam@49: Request newCall(uint64_t interfaceId, uint16_t methodId, cannam@49: kj::Maybe sizeHint); cannam@49: cannam@49: private: cannam@49: kj::Own hook; cannam@49: cannam@49: static kj::Own makeLocalClient(kj::Own&& server); cannam@49: cannam@49: template cannam@49: friend struct _::PointerHelpers; cannam@49: friend struct DynamicCapability; cannam@49: friend class Orphanage; cannam@49: friend struct DynamicStruct; cannam@49: friend struct DynamicList; cannam@49: template cannam@49: friend struct List; cannam@49: friend class _::CapabilityServerSetBase; cannam@49: friend class ClientHook; cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: // Capability servers cannam@49: cannam@49: class CallContextHook; cannam@49: cannam@49: template cannam@49: class CallContext: public kj::DisallowConstCopy { cannam@49: // Wrapper around CallContextHook with a specific return type. cannam@49: // cannam@49: // Methods of this class may only be called from within the server's event loop, not from other cannam@49: // threads. cannam@49: // cannam@49: // The CallContext becomes invalid as soon as the call reports completion. cannam@49: cannam@49: public: cannam@49: explicit CallContext(CallContextHook& hook); cannam@49: cannam@49: typename Params::Reader getParams(); cannam@49: // Get the params payload. cannam@49: cannam@49: void releaseParams(); cannam@49: // Release the params payload. getParams() will throw an exception after this is called. cannam@49: // Releasing the params may allow the RPC system to free up buffer space to handle other cannam@49: // requests. Long-running asynchronous methods should try to call this as early as is cannam@49: // convenient. cannam@49: cannam@49: typename Results::Builder getResults(kj::Maybe sizeHint = nullptr); cannam@49: typename Results::Builder initResults(kj::Maybe sizeHint = nullptr); cannam@49: void setResults(typename Results::Reader value); cannam@49: void adoptResults(Orphan&& value); cannam@49: Orphanage getResultsOrphanage(kj::Maybe sizeHint = nullptr); cannam@49: // Manipulate the results payload. The "Return" message (part of the RPC protocol) will cannam@49: // typically be allocated the first time one of these is called. Some RPC systems may cannam@49: // allocate these messages in a limited space (such as a shared memory segment), therefore the cannam@49: // application should delay calling these as long as is convenient to do so (but don't delay cannam@49: // if doing so would require extra copies later). cannam@49: // cannam@49: // `sizeHint` indicates a guess at the message size. This will usually be used to decide how cannam@49: // much space to allocate for the first message segment (don't worry: only space that is actually cannam@49: // used will be sent on the wire). If omitted, the system decides. The message root pointer cannam@49: // should not be included in the size. So, if you are simply going to copy some existing message cannam@49: // directly into the results, just call `.totalSize()` and pass that in. cannam@49: cannam@49: template cannam@49: kj::Promise tailCall(Request&& tailRequest); cannam@49: // Resolve the call by making a tail call. `tailRequest` is a request that has been filled in cannam@49: // but not yet sent. The context will send the call, then fill in the results with the result cannam@49: // of the call. If tailCall() is used, {get,init,set,adopt}Results (above) *must not* be called. cannam@49: // cannam@49: // The RPC implementation may be able to optimize a tail call to another machine such that the cannam@49: // results never actually pass through this machine. Even if no such optimization is possible, cannam@49: // `tailCall()` may allow pipelined calls to be forwarded optimistically to the new call site. cannam@49: // cannam@49: // In general, this should be the last thing a method implementation calls, and the promise cannam@49: // returned from `tailCall()` should then be returned by the method implementation. cannam@49: cannam@49: void allowCancellation(); cannam@49: // Indicate that it is OK for the RPC system to discard its Promise for this call's result if cannam@49: // the caller cancels the call, thereby transitively canceling any asynchronous operations the cannam@49: // call implementation was performing. This is not done by default because it could represent a cannam@49: // security risk: applications must be carefully written to ensure that they do not end up in cannam@49: // a bad state if an operation is canceled at an arbitrary point. However, for long-running cannam@49: // method calls that hold significant resources, prompt cancellation is often useful. cannam@49: // cannam@49: // Keep in mind that asynchronous cancellation cannot occur while the method is synchronously cannam@49: // executing on a local thread. The method must perform an asynchronous operation or call cannam@49: // `EventLoop::current().evalLater()` to yield control. cannam@49: // cannam@49: // Note: You might think that we should offer `onCancel()` and/or `isCanceled()` methods that cannam@49: // provide notification when the caller cancels the request without forcefully killing off the cannam@49: // promise chain. Unfortunately, this composes poorly with promise forking: the canceled cannam@49: // path may be just one branch of a fork of the result promise. The other branches still want cannam@49: // the call to continue. Promise forking is used within the Cap'n Proto implementation -- in cannam@49: // particular each pipelined call forks the result promise. So, if a caller made a pipelined cannam@49: // call and then dropped the original object, the call should not be canceled, but it would be cannam@49: // excessively complicated for the framework to avoid notififying of cancellation as long as cannam@49: // pipelined calls still exist. cannam@49: cannam@49: private: cannam@49: CallContextHook* hook; cannam@49: cannam@49: friend class Capability::Server; cannam@49: friend struct DynamicCapability; cannam@49: }; cannam@49: cannam@49: class Capability::Server { cannam@49: // Objects implementing a Cap'n Proto interface must subclass this. Typically, such objects cannam@49: // will instead subclass a typed Server interface which will take care of implementing cannam@49: // dispatchCall(). cannam@49: cannam@49: public: cannam@49: typedef Capability Serves; cannam@49: cannam@49: virtual kj::Promise dispatchCall(uint64_t interfaceId, uint16_t methodId, cannam@49: CallContext context) = 0; cannam@49: // Call the given method. `params` is the input struct, and should be released as soon as it cannam@49: // is no longer needed. `context` may be used to allocate the output struct and deal with cannam@49: // cancellation. cannam@49: cannam@49: // TODO(someday): Method which can optionally be overridden to implement Join when the object is cannam@49: // a proxy. cannam@49: cannam@49: protected: cannam@49: inline Capability::Client thisCap(); cannam@49: // Get a capability pointing to this object, much like the `this` keyword. cannam@49: // cannam@49: // The effect of this method is undefined if: cannam@49: // - No capability client has been created pointing to this object. (This is always the case in cannam@49: // the server's constructor.) cannam@49: // - The capability client pointing at this object has been destroyed. (This is always the case cannam@49: // in the server's destructor.) cannam@49: // - Multiple capability clients have been created around the same server (possible if the server cannam@49: // is refcounted, which is not recommended since the client itself provides refcounting). cannam@49: cannam@49: template cannam@49: CallContext internalGetTypedContext( cannam@49: CallContext typeless); cannam@49: kj::Promise internalUnimplemented(const char* actualInterfaceName, cannam@49: uint64_t requestedTypeId); cannam@49: kj::Promise internalUnimplemented(const char* interfaceName, cannam@49: uint64_t typeId, uint16_t methodId); cannam@49: kj::Promise internalUnimplemented(const char* interfaceName, const char* methodName, cannam@49: uint64_t typeId, uint16_t methodId); cannam@49: cannam@49: private: cannam@49: ClientHook* thisHook = nullptr; cannam@49: friend class LocalClient; cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: cannam@49: class ReaderCapabilityTable: private _::CapTableReader { cannam@49: // Class which imbues Readers with the ability to read capabilities. cannam@49: // cannam@49: // In Cap'n Proto format, the encoding of a capability pointer is simply an integer index into cannam@49: // an external table. Since these pointers fundamentally point outside the message, a cannam@49: // MessageReader by default has no idea what they point at, and therefore reading capabilities cannam@49: // from such a reader will throw exceptions. cannam@49: // cannam@49: // In order to be able to read capabilities, you must first attach a capability table, using cannam@49: // this class. By "imbuing" a Reader, you get a new Reader which will interpret capability cannam@49: // pointers by treating them as indexes into the ReaderCapabilityTable. cannam@49: // cannam@49: // Note that when using Cap'n Proto's RPC system, this is handled automatically. cannam@49: cannam@49: public: cannam@49: explicit ReaderCapabilityTable(kj::Array>> table); cannam@49: KJ_DISALLOW_COPY(ReaderCapabilityTable); cannam@49: cannam@49: template cannam@49: T imbue(T reader); cannam@49: // Return a reader equivalent to `reader` except that when reading capability-valued fields, cannam@49: // the capabilities are looked up in this table. cannam@49: cannam@49: private: cannam@49: kj::Array>> table; cannam@49: cannam@49: kj::Maybe> extractCap(uint index) override; cannam@49: }; cannam@49: cannam@49: class BuilderCapabilityTable: private _::CapTableBuilder { cannam@49: // Class which imbues Builders with the ability to read and write capabilities. cannam@49: // cannam@49: // This is much like ReaderCapabilityTable, except for builders. The table starts out empty, cannam@49: // but capabilities can be added to it over time. cannam@49: cannam@49: public: cannam@49: BuilderCapabilityTable(); cannam@49: KJ_DISALLOW_COPY(BuilderCapabilityTable); cannam@49: cannam@49: inline kj::ArrayPtr>> getTable() { return table; } cannam@49: cannam@49: template cannam@49: T imbue(T builder); cannam@49: // Return a builder equivalent to `builder` except that when reading capability-valued fields, cannam@49: // the capabilities are looked up in this table. cannam@49: cannam@49: private: cannam@49: kj::Vector>> table; cannam@49: cannam@49: kj::Maybe> extractCap(uint index) override; cannam@49: uint injectCap(kj::Own&& cap) override; cannam@49: void dropCap(uint index) override; cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: cannam@49: namespace _ { // private cannam@49: cannam@49: class CapabilityServerSetBase { cannam@49: public: cannam@49: Capability::Client addInternal(kj::Own&& server, void* ptr); cannam@49: kj::Promise getLocalServerInternal(Capability::Client& client); cannam@49: }; cannam@49: cannam@49: } // namespace _ (private) cannam@49: cannam@49: template cannam@49: class CapabilityServerSet: private _::CapabilityServerSetBase { cannam@49: // Allows a server to recognize its own capabilities when passed back to it, and obtain the cannam@49: // underlying Server objects associated with them. cannam@49: // cannam@49: // All objects in the set must have the same interface type T. The objects may implement various cannam@49: // interfaces derived from T (and in fact T can be `capnp::Capability` to accept all objects), cannam@49: // but note that if you compile with RTTI disabled then you will not be able to down-cast through cannam@49: // virtual inheritance, and all inheritance between server interfaces is virtual. So, with RTTI cannam@49: // disabled, you will likely need to set T to be the most-derived Cap'n Proto interface type, cannam@49: // and you server class will need to be directly derived from that, so that you can use cannam@49: // static_cast (or kj::downcast) to cast to it after calling getLocalServer(). (If you compile cannam@49: // with RTTI, then you can freely dynamic_cast and ignore this issue!) cannam@49: cannam@49: public: cannam@49: CapabilityServerSet() = default; cannam@49: KJ_DISALLOW_COPY(CapabilityServerSet); cannam@49: cannam@49: typename T::Client add(kj::Own&& server); cannam@49: // Create a new capability Client for the given Server and also add this server to the set. cannam@49: cannam@49: kj::Promise> getLocalServer(typename T::Client& client); cannam@49: // Given a Client pointing to a server previously passed to add(), return the corresponding cannam@49: // Server. This returns a promise because if the input client is itself a promise, this must cannam@49: // wait for it to resolve. Keep in mind that the server will be deleted when all clients are cannam@49: // gone, so the caller should make sure to keep the client alive (hence why this method only cannam@49: // accepts an lvalue input). cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: // Hook interfaces which must be implemented by the RPC system. Applications never call these cannam@49: // directly; the RPC system implements them and the types defined earlier in this file wrap them. cannam@49: cannam@49: class RequestHook { cannam@49: // Hook interface implemented by RPC system representing a request being built. cannam@49: cannam@49: public: cannam@49: virtual RemotePromise send() = 0; cannam@49: // Send the call and return a promise for the result. cannam@49: cannam@49: virtual const void* getBrand() = 0; cannam@49: // Returns a void* that identifies who made this request. This can be used by an RPC adapter to cannam@49: // discover when tail call is going to be sent over its own connection and therefore can be cannam@49: // optimized into a remote tail call. cannam@49: cannam@49: template cannam@49: inline static kj::Own from(Request&& request) { cannam@49: return kj::mv(request.hook); cannam@49: } cannam@49: }; cannam@49: cannam@49: class ResponseHook { cannam@49: // Hook interface implemented by RPC system representing a response. cannam@49: // cannam@49: // At present this class has no methods. It exists only for garbage collection -- when the cannam@49: // ResponseHook is destroyed, the results can be freed. cannam@49: cannam@49: public: cannam@49: virtual ~ResponseHook() noexcept(false); cannam@49: // Just here to make sure the type is dynamic. cannam@49: cannam@49: template cannam@49: inline static kj::Own from(Response&& response) { cannam@49: return kj::mv(response.hook); cannam@49: } cannam@49: }; cannam@49: cannam@49: // class PipelineHook is declared in any.h because it is needed there. cannam@49: cannam@49: class ClientHook { cannam@49: public: cannam@49: ClientHook(); cannam@49: cannam@49: virtual Request newCall( cannam@49: uint64_t interfaceId, uint16_t methodId, kj::Maybe sizeHint) = 0; cannam@49: // Start a new call, allowing the client to allocate request/response objects as it sees fit. cannam@49: // This version is used when calls are made from application code in the local process. cannam@49: cannam@49: struct VoidPromiseAndPipeline { cannam@49: kj::Promise promise; cannam@49: kj::Own pipeline; cannam@49: }; cannam@49: cannam@49: virtual VoidPromiseAndPipeline call(uint64_t interfaceId, uint16_t methodId, cannam@49: kj::Own&& context) = 0; cannam@49: // Call the object, but the caller controls allocation of the request/response objects. If the cannam@49: // callee insists on allocating these objects itself, it must make a copy. This version is used cannam@49: // when calls come in over the network via an RPC system. Note that even if the returned cannam@49: // `Promise` is discarded, the call may continue executing if any pipelined calls are cannam@49: // waiting for it. cannam@49: // cannam@49: // Since the caller of this method chooses the CallContext implementation, it is the caller's cannam@49: // responsibility to ensure that the returned promise is not canceled unless allowed via cannam@49: // the context's `allowCancellation()`. cannam@49: // cannam@49: // The call must not begin synchronously; the callee must arrange for the call to begin in a cannam@49: // later turn of the event loop. Otherwise, application code may call back and affect the cannam@49: // callee's state in an unexpected way. cannam@49: cannam@49: virtual kj::Maybe getResolved() = 0; cannam@49: // If this ClientHook is a promise that has already resolved, returns the inner, resolved version cannam@49: // of the capability. The caller may permanently replace this client with the resolved one if cannam@49: // desired. Returns null if the client isn't a promise or hasn't resolved yet -- use cannam@49: // `whenMoreResolved()` to distinguish between them. cannam@49: cannam@49: virtual kj::Maybe>> whenMoreResolved() = 0; cannam@49: // If this client is a settled reference (not a promise), return nullptr. Otherwise, return a cannam@49: // promise that eventually resolves to a new client that is closer to being the final, settled cannam@49: // client (i.e. the value eventually returned by `getResolved()`). Calling this repeatedly cannam@49: // should eventually produce a settled client. cannam@49: cannam@49: kj::Promise whenResolved(); cannam@49: // Repeatedly calls whenMoreResolved() until it returns nullptr. cannam@49: cannam@49: virtual kj::Own addRef() = 0; cannam@49: // Return a new reference to the same capability. cannam@49: cannam@49: virtual const void* getBrand() = 0; cannam@49: // Returns a void* that identifies who made this client. This can be used by an RPC adapter to cannam@49: // discover when a capability it needs to marshal is one that it created in the first place, and cannam@49: // therefore it can transfer the capability without proxying. cannam@49: cannam@49: static const uint NULL_CAPABILITY_BRAND; cannam@49: // Value is irrelevant; used for pointer. cannam@49: cannam@49: inline bool isNull() { return getBrand() == &NULL_CAPABILITY_BRAND; } cannam@49: // Returns true if the capability was created as a result of assigning a Client to null or by cannam@49: // reading a null pointer out of a Cap'n Proto message. cannam@49: cannam@49: virtual void* getLocalServer(_::CapabilityServerSetBase& capServerSet); cannam@49: // If this is a local capability created through `capServerSet`, return the underlying Server. cannam@49: // Otherwise, return nullptr. Default implementation (which everyone except LocalClient should cannam@49: // use) always returns nullptr. cannam@49: cannam@49: static kj::Own from(Capability::Client client) { return kj::mv(client.hook); } cannam@49: }; cannam@49: cannam@49: class CallContextHook { cannam@49: // Hook interface implemented by RPC system to manage a call on the server side. See cannam@49: // CallContext. cannam@49: cannam@49: public: cannam@49: virtual AnyPointer::Reader getParams() = 0; cannam@49: virtual void releaseParams() = 0; cannam@49: virtual AnyPointer::Builder getResults(kj::Maybe sizeHint) = 0; cannam@49: virtual kj::Promise tailCall(kj::Own&& request) = 0; cannam@49: virtual void allowCancellation() = 0; cannam@49: cannam@49: virtual kj::Promise onTailCall() = 0; cannam@49: // If `tailCall()` is called, resolves to the PipelineHook from the tail call. An cannam@49: // implementation of `ClientHook::call()` is allowed to call this at most once. cannam@49: cannam@49: virtual ClientHook::VoidPromiseAndPipeline directTailCall(kj::Own&& request) = 0; cannam@49: // Call this when you would otherwise call onTailCall() immediately followed by tailCall(). cannam@49: // Implementations of tailCall() should typically call directTailCall() and then fulfill the cannam@49: // promise fulfiller for onTailCall() with the returned pipeline. cannam@49: cannam@49: virtual kj::Own addRef() = 0; cannam@49: }; cannam@49: cannam@49: kj::Own newLocalPromiseClient(kj::Promise>&& promise); cannam@49: // Returns a ClientHook that queues up calls until `promise` resolves, then forwards them to cannam@49: // the new client. This hook's `getResolved()` and `whenMoreResolved()` methods will reflect the cannam@49: // redirection to the eventual replacement client. cannam@49: cannam@49: kj::Own newLocalPromisePipeline(kj::Promise>&& promise); cannam@49: // Returns a PipelineHook that queues up calls until `promise` resolves, then forwards them to cannam@49: // the new pipeline. cannam@49: cannam@49: kj::Own newBrokenCap(kj::StringPtr reason); cannam@49: kj::Own newBrokenCap(kj::Exception&& reason); cannam@49: // Helper function that creates a capability which simply throws exceptions when called. cannam@49: cannam@49: kj::Own newBrokenPipeline(kj::Exception&& reason); cannam@49: // Helper function that creates a pipeline which simply throws exceptions when called. cannam@49: cannam@49: Request newBrokenRequest( cannam@49: kj::Exception&& reason, kj::Maybe sizeHint); cannam@49: // Helper function that creates a Request object that simply throws exceptions when sent. cannam@49: cannam@49: // ======================================================================================= cannam@49: // Extend PointerHelpers for interfaces cannam@49: cannam@49: namespace _ { // private cannam@49: cannam@49: template cannam@49: struct PointerHelpers { cannam@49: static inline typename T::Client get(PointerReader reader) { cannam@49: return typename T::Client(reader.getCapability()); cannam@49: } cannam@49: static inline typename T::Client get(PointerBuilder builder) { cannam@49: return typename T::Client(builder.getCapability()); cannam@49: } cannam@49: static inline void set(PointerBuilder builder, typename T::Client&& value) { cannam@49: builder.setCapability(kj::mv(value.Capability::Client::hook)); cannam@49: } cannam@49: static inline void set(PointerBuilder builder, typename T::Client& value) { cannam@49: builder.setCapability(value.Capability::Client::hook->addRef()); cannam@49: } cannam@49: static inline void adopt(PointerBuilder builder, Orphan&& value) { cannam@49: builder.adopt(kj::mv(value.builder)); cannam@49: } cannam@49: static inline Orphan disown(PointerBuilder builder) { cannam@49: return Orphan(builder.disown()); cannam@49: } cannam@49: }; cannam@49: cannam@49: } // namespace _ (private) cannam@49: cannam@49: // ======================================================================================= cannam@49: // Extend List for interfaces cannam@49: cannam@49: template cannam@49: struct List { cannam@49: List() = delete; cannam@49: cannam@49: class Reader { cannam@49: public: cannam@49: typedef List Reads; cannam@49: cannam@49: Reader() = default; cannam@49: inline explicit Reader(_::ListReader reader): reader(reader) {} cannam@49: cannam@49: inline uint size() const { return reader.size() / ELEMENTS; } cannam@49: inline typename T::Client operator[](uint index) const { cannam@49: KJ_IREQUIRE(index < size()); cannam@49: return typename T::Client(reader.getPointerElement(index * ELEMENTS).getCapability()); cannam@49: } cannam@49: cannam@49: typedef _::IndexingIterator Iterator; cannam@49: inline Iterator begin() const { return Iterator(this, 0); } cannam@49: inline Iterator end() const { return Iterator(this, size()); } cannam@49: cannam@49: private: cannam@49: _::ListReader reader; cannam@49: template cannam@49: friend struct _::PointerHelpers; cannam@49: template cannam@49: friend struct List; cannam@49: friend class Orphanage; cannam@49: template cannam@49: friend struct ToDynamic_; cannam@49: }; cannam@49: cannam@49: class Builder { cannam@49: public: cannam@49: typedef List Builds; cannam@49: cannam@49: Builder() = delete; cannam@49: inline Builder(decltype(nullptr)) {} cannam@49: inline explicit Builder(_::ListBuilder builder): builder(builder) {} cannam@49: cannam@49: inline operator Reader() const { return Reader(builder.asReader()); } cannam@49: inline Reader asReader() const { return Reader(builder.asReader()); } cannam@49: cannam@49: inline uint size() const { return builder.size() / ELEMENTS; } cannam@49: inline typename T::Client operator[](uint index) { cannam@49: KJ_IREQUIRE(index < size()); cannam@49: return typename T::Client(builder.getPointerElement(index * ELEMENTS).getCapability()); cannam@49: } cannam@49: inline void set(uint index, typename T::Client value) { cannam@49: KJ_IREQUIRE(index < size()); cannam@49: builder.getPointerElement(index * ELEMENTS).setCapability(kj::mv(value.hook)); cannam@49: } cannam@49: inline void adopt(uint index, Orphan&& value) { cannam@49: KJ_IREQUIRE(index < size()); cannam@49: builder.getPointerElement(index * ELEMENTS).adopt(kj::mv(value)); cannam@49: } cannam@49: inline Orphan disown(uint index) { cannam@49: KJ_IREQUIRE(index < size()); cannam@49: return Orphan(builder.getPointerElement(index * ELEMENTS).disown()); cannam@49: } cannam@49: cannam@49: typedef _::IndexingIterator Iterator; cannam@49: inline Iterator begin() { return Iterator(this, 0); } cannam@49: inline Iterator end() { return Iterator(this, size()); } cannam@49: cannam@49: private: cannam@49: _::ListBuilder builder; cannam@49: friend class Orphanage; cannam@49: template cannam@49: friend struct ToDynamic_; cannam@49: }; cannam@49: cannam@49: private: cannam@49: inline static _::ListBuilder initPointer(_::PointerBuilder builder, uint size) { cannam@49: return builder.initList(ElementSize::POINTER, size * ELEMENTS); cannam@49: } cannam@49: inline static _::ListBuilder getFromPointer(_::PointerBuilder builder, const word* defaultValue) { cannam@49: return builder.getList(ElementSize::POINTER, defaultValue); cannam@49: } cannam@49: inline static _::ListReader getFromPointer( cannam@49: const _::PointerReader& reader, const word* defaultValue) { cannam@49: return reader.getList(ElementSize::POINTER, defaultValue); cannam@49: } cannam@49: cannam@49: template cannam@49: friend struct List; cannam@49: template cannam@49: friend struct _::PointerHelpers; cannam@49: }; cannam@49: cannam@49: // ======================================================================================= cannam@49: // Inline implementation details cannam@49: cannam@49: template cannam@49: RemotePromise Request::send() { cannam@49: auto typelessPromise = hook->send(); cannam@49: hook = nullptr; // prevent reuse cannam@49: cannam@49: // Convert the Promise to return the correct response type. cannam@49: // Explicitly upcast to kj::Promise to make clear that calling .then() doesn't invalidate the cannam@49: // Pipeline part of the RemotePromise. cannam@49: auto typedPromise = kj::implicitCast>&>(typelessPromise) cannam@49: .then([](Response&& response) -> Response { cannam@49: return Response(response.getAs(), kj::mv(response.hook)); cannam@49: }); cannam@49: cannam@49: // Wrap the typeless pipeline in a typed wrapper. cannam@49: typename Results::Pipeline typedPipeline( cannam@49: kj::mv(kj::implicitCast(typelessPromise))); cannam@49: cannam@49: return RemotePromise(kj::mv(typedPromise), kj::mv(typedPipeline)); cannam@49: } cannam@49: cannam@49: inline Capability::Client::Client(kj::Own&& hook): hook(kj::mv(hook)) {} cannam@49: template cannam@49: inline Capability::Client::Client(kj::Own&& server) cannam@49: : hook(makeLocalClient(kj::mv(server))) {} cannam@49: template cannam@49: inline Capability::Client::Client(kj::Promise&& promise) cannam@49: : hook(newLocalPromiseClient(promise.then([](T&& t) { return kj::mv(t.hook); }))) {} cannam@49: inline Capability::Client::Client(Client& other): hook(other.hook->addRef()) {} cannam@49: inline Capability::Client& Capability::Client::operator=(Client& other) { cannam@49: hook = other.hook->addRef(); cannam@49: return *this; cannam@49: } cannam@49: template cannam@49: inline typename T::Client Capability::Client::castAs() { cannam@49: return typename T::Client(hook->addRef()); cannam@49: } cannam@49: inline kj::Promise Capability::Client::whenResolved() { cannam@49: return hook->whenResolved(); cannam@49: } cannam@49: inline Request Capability::Client::typelessRequest( cannam@49: uint64_t interfaceId, uint16_t methodId, cannam@49: kj::Maybe sizeHint) { cannam@49: return newCall(interfaceId, methodId, sizeHint); cannam@49: } cannam@49: template cannam@49: inline Request Capability::Client::newCall( cannam@49: uint64_t interfaceId, uint16_t methodId, kj::Maybe sizeHint) { cannam@49: auto typeless = hook->newCall(interfaceId, methodId, sizeHint); cannam@49: return Request(typeless.template getAs(), kj::mv(typeless.hook)); cannam@49: } cannam@49: cannam@49: template cannam@49: inline CallContext::CallContext(CallContextHook& hook): hook(&hook) {} cannam@49: template cannam@49: inline typename Params::Reader CallContext::getParams() { cannam@49: return hook->getParams().template getAs(); cannam@49: } cannam@49: template cannam@49: inline void CallContext::releaseParams() { cannam@49: hook->releaseParams(); cannam@49: } cannam@49: template cannam@49: inline typename Results::Builder CallContext::getResults( cannam@49: kj::Maybe sizeHint) { cannam@49: // `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401 cannam@49: return hook->getResults(sizeHint).template getAs(); cannam@49: } cannam@49: template cannam@49: inline typename Results::Builder CallContext::initResults( cannam@49: kj::Maybe sizeHint) { cannam@49: // `template` keyword needed due to: http://llvm.org/bugs/show_bug.cgi?id=17401 cannam@49: return hook->getResults(sizeHint).template initAs(); cannam@49: } cannam@49: template cannam@49: inline void CallContext::setResults(typename Results::Reader value) { cannam@49: hook->getResults(value.totalSize()).template setAs(value); cannam@49: } cannam@49: template cannam@49: inline void CallContext::adoptResults(Orphan&& value) { cannam@49: hook->getResults(nullptr).adopt(kj::mv(value)); cannam@49: } cannam@49: template cannam@49: inline Orphanage CallContext::getResultsOrphanage( cannam@49: kj::Maybe sizeHint) { cannam@49: return Orphanage::getForMessageContaining(hook->getResults(sizeHint)); cannam@49: } cannam@49: template cannam@49: template cannam@49: inline kj::Promise CallContext::tailCall( cannam@49: Request&& tailRequest) { cannam@49: return hook->tailCall(kj::mv(tailRequest.hook)); cannam@49: } cannam@49: template cannam@49: inline void CallContext::allowCancellation() { cannam@49: hook->allowCancellation(); cannam@49: } cannam@49: cannam@49: template cannam@49: CallContext Capability::Server::internalGetTypedContext( cannam@49: CallContext typeless) { cannam@49: return CallContext(*typeless.hook); cannam@49: } cannam@49: cannam@49: Capability::Client Capability::Server::thisCap() { cannam@49: return Client(thisHook->addRef()); cannam@49: } cannam@49: cannam@49: template cannam@49: T ReaderCapabilityTable::imbue(T reader) { cannam@49: return T(_::PointerHelpers>::getInternalReader(reader).imbue(this)); cannam@49: } cannam@49: cannam@49: template cannam@49: T BuilderCapabilityTable::imbue(T builder) { cannam@49: return T(_::PointerHelpers>::getInternalBuilder(kj::mv(builder)).imbue(this)); cannam@49: } cannam@49: cannam@49: template cannam@49: typename T::Client CapabilityServerSet::add(kj::Own&& server) { cannam@49: void* ptr = reinterpret_cast(server.get()); cannam@49: // Clang insists that `castAs` is a template-dependent member and therefore we need the cannam@49: // `template` keyword here, but AFAICT this is wrong: addImpl() is not a template. cannam@49: return addInternal(kj::mv(server), ptr).template castAs(); cannam@49: } cannam@49: cannam@49: template cannam@49: kj::Promise> CapabilityServerSet::getLocalServer( cannam@49: typename T::Client& client) { cannam@49: return getLocalServerInternal(client) cannam@49: .then([](void* server) -> kj::Maybe { cannam@49: if (server == nullptr) { cannam@49: return nullptr; cannam@49: } else { cannam@49: return *reinterpret_cast(server); cannam@49: } cannam@49: }); cannam@49: } cannam@49: cannam@49: template cannam@49: struct Orphanage::GetInnerReader { cannam@49: static inline kj::Own apply(typename T::Client t) { cannam@49: return ClientHook::from(kj::mv(t)); cannam@49: } cannam@49: }; cannam@49: cannam@49: } // namespace capnp cannam@49: cannam@49: #endif // CAPNP_CAPABILITY_H_