//===------- RPCUTils.h - Utilities for building RPC APIs -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Utilities to support construction of simple RPC APIs. // // The RPC utilities aim for ease of use (minimal conceptual overhead) for C++ // programmers, high performance, low memory overhead, and efficient use of the // communications channel. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H #define LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H #include #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/ExecutionEngine/Orc/RPCSerialization.h" #ifdef _MSC_VER // concrt.h depends on eh.h for __uncaught_exception declaration // even if we disable exceptions. #include // Disable warnings from ppltasks.h transitively included by . #pragma warning(push) #pragma warning(disable : 4530) #pragma warning(disable : 4062) #endif #include #ifdef _MSC_VER #pragma warning(pop) #endif namespace llvm { namespace orc { namespace rpc { template class Function; // RPC Function class. // DerivedFunc should be a user defined class with a static 'getName()' method // returning a const char* representing the function's name. template class Function { public: /// User defined function type. using Type = RetT(ArgTs...); /// Return type. using ReturnType = RetT; /// Returns the full function prototype as a string. static const char *getPrototype() { std::lock_guard Lock(NameMutex); if (Name.empty()) raw_string_ostream(Name) << RPCTypeName::getName() << " " << DerivedFunc::getName() << "(" << llvm::orc::rpc::RPCTypeNameSequence() << ")"; return Name.data(); } private: static std::mutex NameMutex; static std::string Name; }; template std::mutex Function::NameMutex; template std::string Function::Name; /// Provides a typedef for a tuple containing the decayed argument types. template class FunctionArgsTuple; template class FunctionArgsTuple { public: using Type = std::tuple::type>::type...>; }; /// Allocates RPC function ids during autonegotiation. /// Specializations of this class must provide four members: /// /// static T getInvalidId(): /// Should return a reserved id that will be used to represent missing /// functions during autonegotiation. /// /// static T getResponseId(): /// Should return a reserved id that will be used to send function responses /// (return values). /// /// static T getNegotiateId(): /// Should return a reserved id for the negotiate function, which will be used /// to negotiate ids for user defined functions. /// /// template T allocate(): /// Allocate a unique id for function Func. template class RPCFunctionIdAllocator; /// This specialization of RPCFunctionIdAllocator provides a default /// implementation for integral types. template class RPCFunctionIdAllocator< T, typename std::enable_if::value>::type> { public: static T getInvalidId() { return T(0); } static T getResponseId() { return T(1); } static T getNegotiateId() { return T(2); } template T allocate() { return NextId++; } private: T NextId = 3; }; namespace detail { // FIXME: Remove MSVCPError/MSVCPExpected once MSVC's future implementation // supports classes without default constructors. #ifdef _MSC_VER namespace msvc_hacks { // Work around MSVC's future implementation's use of default constructors: // A default constructed value in the promise will be overwritten when the // real error is set - so the default constructed Error has to be checked // already. class MSVCPError : public Error { public: MSVCPError() { (void)!!*this; } MSVCPError(MSVCPError &&Other) : Error(std::move(Other)) {} MSVCPError &operator=(MSVCPError Other) { Error::operator=(std::move(Other)); return *this; } MSVCPError(Error Err) : Error(std::move(Err)) {} }; // Work around MSVC's future implementation, similar to MSVCPError. template class MSVCPExpected : public Expected { public: MSVCPExpected() : Expected(make_error("", inconvertibleErrorCode())) { consumeError(this->takeError()); } MSVCPExpected(MSVCPExpected &&Other) : Expected(std::move(Other)) {} MSVCPExpected &operator=(MSVCPExpected &&Other) { Expected::operator=(std::move(Other)); return *this; } MSVCPExpected(Error Err) : Expected(std::move(Err)) {} template MSVCPExpected( OtherT &&Val, typename std::enable_if::value>::type * = nullptr) : Expected(std::move(Val)) {} template MSVCPExpected( Expected &&Other, typename std::enable_if::value>::type * = nullptr) : Expected(std::move(Other)) {} template explicit MSVCPExpected( Expected &&Other, typename std::enable_if::value>::type * = nullptr) : Expected(std::move(Other)) {} }; } // end namespace msvc_hacks #endif // _MSC_VER // ResultTraits provides typedefs and utilities specific to the return type // of functions. template class ResultTraits { public: // The return type wrapped in llvm::Expected. using ErrorReturnType = Expected; #ifdef _MSC_VER // The ErrorReturnType wrapped in a std::promise. using ReturnPromiseType = std::promise>; // The ErrorReturnType wrapped in a std::future. using ReturnFutureType = std::future>; #else // The ErrorReturnType wrapped in a std::promise. using ReturnPromiseType = std::promise; // The ErrorReturnType wrapped in a std::future. using ReturnFutureType = std::future; #endif // Create a 'blank' value of the ErrorReturnType, ready and safe to // overwrite. static ErrorReturnType createBlankErrorReturnValue() { return ErrorReturnType(RetT()); } // Consume an abandoned ErrorReturnType. static void consumeAbandoned(ErrorReturnType RetOrErr) { consumeError(RetOrErr.takeError()); } }; // ResultTraits specialization for void functions. template <> class ResultTraits { public: // For void functions, ErrorReturnType is llvm::Error. using ErrorReturnType = Error; #ifdef _MSC_VER // The ErrorReturnType wrapped in a std::promise. using ReturnPromiseType = std::promise; // The ErrorReturnType wrapped in a std::future. using ReturnFutureType = std::future; #else // The ErrorReturnType wrapped in a std::promise. using ReturnPromiseType = std::promise; // The ErrorReturnType wrapped in a std::future. using ReturnFutureType = std::future; #endif // Create a 'blank' value of the ErrorReturnType, ready and safe to // overwrite. static ErrorReturnType createBlankErrorReturnValue() { return ErrorReturnType::success(); } // Consume an abandoned ErrorReturnType. static void consumeAbandoned(ErrorReturnType Err) { consumeError(std::move(Err)); } }; // ResultTraits is equivalent to ResultTraits. This allows // handlers for void RPC functions to return either void (in which case they // implicitly succeed) or Error (in which case their error return is // propagated). See usage in HandlerTraits::runHandlerHelper. template <> class ResultTraits : public ResultTraits {}; // ResultTraits> is equivalent to ResultTraits. This allows // handlers for RPC functions returning a T to return either a T (in which // case they implicitly succeed) or Expected (in which case their error // return is propagated). See usage in HandlerTraits::runHandlerHelper. template class ResultTraits> : public ResultTraits {}; // Send a response of the given wire return type (WireRetT) over the // channel, with the given sequence number. template static Error respond(ChannelT &C, const FunctionIdT &ResponseId, SequenceNumberT SeqNo, Expected ResultOrErr) { // If this was an error bail out. // FIXME: Send an "error" message to the client if this is not a channel // failure? if (auto Err = ResultOrErr.takeError()) return Err; // Open the response message. if (auto Err = C.startSendMessage(ResponseId, SeqNo)) return Err; // Serialize the result. if (auto Err = SerializationTraits::serialize( C, *ResultOrErr)) return Err; // Close the response message. return C.endSendMessage(); } // Send an empty response message on the given channel to indicate that // the handler ran. template static Error respond(ChannelT &C, const FunctionIdT &ResponseId, SequenceNumberT SeqNo, Error Err) { if (Err) return Err; if (auto Err2 = C.startSendMessage(ResponseId, SeqNo)) return Err2; return C.endSendMessage(); } // Converts a given type to the equivalent error return type. template class WrappedHandlerReturn { public: using Type = Expected; }; template class WrappedHandlerReturn> { public: using Type = Expected; }; template <> class WrappedHandlerReturn { public: using Type = Error; }; template <> class WrappedHandlerReturn { public: using Type = Error; }; template <> class WrappedHandlerReturn { public: using Type = Error; }; // This template class provides utilities related to RPC function handlers. // The base case applies to non-function types (the template class is // specialized for function types) and inherits from the appropriate // speciilization for the given non-function type's call operator. template class HandlerTraits : public HandlerTraits::type::operator())> { }; // Traits for handlers with a given function type. template class HandlerTraits { public: // Function type of the handler. using Type = RetT(ArgTs...); // Return type of the handler. using ReturnType = RetT; // A std::tuple wrapping the handler arguments. using ArgStorage = typename FunctionArgsTuple::Type; // Call the given handler with the given arguments. template static typename WrappedHandlerReturn::Type unpackAndRun(HandlerT &Handler, ArgStorage &Args) { return unpackAndRunHelper(Handler, Args, llvm::index_sequence_for()); } // Call the given handler with the given arguments. template static typename std::enable_if< std::is_void::ReturnType>::value, Error>::type run(HandlerT &Handler, ArgTs &&... Args) { Handler(std::move(Args)...); return Error::success(); } template static typename std::enable_if< !std::is_void::ReturnType>::value, typename HandlerTraits::ReturnType>::type run(HandlerT &Handler, ArgTs... Args) { return Handler(std::move(Args)...); } // Serialize arguments to the channel. template static Error serializeArgs(ChannelT &C, const CArgTs... CArgs) { return SequenceSerialization::serialize(C, CArgs...); } // Deserialize arguments from the channel. template static Error deserializeArgs(ChannelT &C, std::tuple &Args) { return deserializeArgsHelper(C, Args, llvm::index_sequence_for()); } private: template static Error deserializeArgsHelper(ChannelT &C, std::tuple &Args, llvm::index_sequence _) { return SequenceSerialization::deserialize( C, std::get(Args)...); } template static typename WrappedHandlerReturn< typename HandlerTraits::ReturnType>::Type unpackAndRunHelper(HandlerT &Handler, ArgStorage &Args, llvm::index_sequence) { return run(Handler, std::move(std::get(Args))...); } }; // Handler traits for class methods (especially call operators for lambdas). template class HandlerTraits : public HandlerTraits {}; // Handler traits for const class methods (especially call operators for // lambdas). template class HandlerTraits : public HandlerTraits {}; // Utility to peel the Expected wrapper off a response handler error type. template class ResponseHandlerArg; template class ResponseHandlerArg)> { public: using ArgType = Expected; using UnwrappedArgType = ArgT; }; template class ResponseHandlerArg)> { public: using ArgType = Expected; using UnwrappedArgType = ArgT; }; template <> class ResponseHandlerArg { public: using ArgType = Error; }; template <> class ResponseHandlerArg { public: using ArgType = Error; }; // ResponseHandler represents a handler for a not-yet-received function call // result. template class ResponseHandler { public: virtual ~ResponseHandler() {} // Reads the function result off the wire and acts on it. The meaning of // "act" will depend on how this method is implemented in any given // ResponseHandler subclass but could, for example, mean running a // user-specified handler or setting a promise value. virtual Error handleResponse(ChannelT &C) = 0; // Abandons this outstanding result. virtual void abandon() = 0; // Create an error instance representing an abandoned response. static Error createAbandonedResponseError() { return orcError(OrcErrorCode::RPCResponseAbandoned); } }; // ResponseHandler subclass for RPC functions with non-void returns. template class ResponseHandlerImpl : public ResponseHandler { public: ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} // Handle the result by deserializing it from the channel then passing it // to the user defined handler. Error handleResponse(ChannelT &C) override { using UnwrappedArgType = typename ResponseHandlerArg< typename HandlerTraits::Type>::UnwrappedArgType; UnwrappedArgType Result; if (auto Err = SerializationTraits::deserialize(C, Result)) return Err; if (auto Err = C.endReceiveMessage()) return Err; return Handler(Result); } // Abandon this response by calling the handler with an 'abandoned response' // error. void abandon() override { if (auto Err = Handler(this->createAbandonedResponseError())) { // Handlers should not fail when passed an abandoned response error. report_fatal_error(std::move(Err)); } } private: HandlerT Handler; }; // ResponseHandler subclass for RPC functions with void returns. template class ResponseHandlerImpl : public ResponseHandler { public: ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} // Handle the result (no actual value, just a notification that the function // has completed on the remote end) by calling the user-defined handler with // Error::success(). Error handleResponse(ChannelT &C) override { if (auto Err = C.endReceiveMessage()) return Err; return Handler(Error::success()); } // Abandon this response by calling the handler with an 'abandoned response' // error. void abandon() override { if (auto Err = Handler(this->createAbandonedResponseError())) { // Handlers should not fail when passed an abandoned response error. report_fatal_error(std::move(Err)); } } private: HandlerT Handler; }; // Create a ResponseHandler from a given user handler. template std::unique_ptr> createResponseHandler(HandlerT H) { return llvm::make_unique>( std::move(H)); } // Helper for wrapping member functions up as functors. This is useful for // installing methods as result handlers. template class MemberFnWrapper { public: using MethodT = RetT (ClassT::*)(ArgTs...); MemberFnWrapper(ClassT &Instance, MethodT Method) : Instance(Instance), Method(Method) {} RetT operator()(ArgTs &&... Args) { return (Instance.*Method)(std::move(Args)...); } private: ClassT &Instance; MethodT Method; }; // Helper that provides a Functor for deserializing arguments. template class ReadArgs { public: Error operator()() { return Error::success(); } }; template class ReadArgs : public ReadArgs { public: ReadArgs(ArgT &Arg, ArgTs &... Args) : ReadArgs(Args...), Arg(Arg) {} Error operator()(ArgT &ArgVal, ArgTs &... ArgVals) { this->Arg = std::move(ArgVal); return ReadArgs::operator()(ArgVals...); } private: ArgT &Arg; }; // Manage sequence numbers. template class SequenceNumberManager { public: // Reset, making all sequence numbers available. void reset() { std::lock_guard Lock(SeqNoLock); NextSequenceNumber = 0; FreeSequenceNumbers.clear(); } // Get the next available sequence number. Will re-use numbers that have // been released. SequenceNumberT getSequenceNumber() { std::lock_guard Lock(SeqNoLock); if (FreeSequenceNumbers.empty()) return NextSequenceNumber++; auto SequenceNumber = FreeSequenceNumbers.back(); FreeSequenceNumbers.pop_back(); return SequenceNumber; } // Release a sequence number, making it available for re-use. void releaseSequenceNumber(SequenceNumberT SequenceNumber) { std::lock_guard Lock(SeqNoLock); FreeSequenceNumbers.push_back(SequenceNumber); } private: std::mutex SeqNoLock; SequenceNumberT NextSequenceNumber = 0; std::vector FreeSequenceNumbers; }; // Checks that predicate P holds for each corresponding pair of type arguments // from T1 and T2 tuple. template