//===- llvm/ExecutionEngine/Orc/RPCSerialization.h --------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H #define LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H #include "OrcError.h" #include "llvm/Support/thread.h" #include #include #include namespace llvm { namespace orc { namespace rpc { template class RPCTypeName; /// TypeNameSequence is a utility for rendering sequences of types to a string /// by rendering each type, separated by ", ". template class RPCTypeNameSequence {}; /// Render an empty TypeNameSequence to an ostream. template OStream &operator<<(OStream &OS, const RPCTypeNameSequence<> &V) { return OS; } /// Render a TypeNameSequence of a single type to an ostream. template OStream &operator<<(OStream &OS, const RPCTypeNameSequence &V) { OS << RPCTypeName::getName(); return OS; } /// Render a TypeNameSequence of more than one type to an ostream. template OStream& operator<<(OStream &OS, const RPCTypeNameSequence &V) { OS << RPCTypeName::getName() << ", " << RPCTypeNameSequence(); return OS; } template <> class RPCTypeName { public: static const char* getName() { return "void"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int8_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint8_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int16_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint16_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int32_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint32_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int64_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint64_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "bool"; } }; template <> class RPCTypeName { public: static const char* getName() { return "std::string"; } }; template <> class RPCTypeName { public: static const char* getName() { return "Error"; } }; template class RPCTypeName> { public: static const char* getName() { std::lock_guard Lock(NameMutex); if (Name.empty()) raw_string_ostream(Name) << "Expected<" << RPCTypeNameSequence() << ">"; return Name.data(); } private: static std::mutex NameMutex; static std::string Name; }; template std::mutex RPCTypeName>::NameMutex; template std::string RPCTypeName>::Name; template class RPCTypeName> { public: static const char* getName() { std::lock_guard Lock(NameMutex); if (Name.empty()) raw_string_ostream(Name) << "std::pair<" << RPCTypeNameSequence() << ">"; return Name.data(); } private: static std::mutex NameMutex; static std::string Name; }; template std::mutex RPCTypeName>::NameMutex; template std::string RPCTypeName>::Name; template class RPCTypeName> { public: static const char* getName() { std::lock_guard Lock(NameMutex); if (Name.empty()) raw_string_ostream(Name) << "std::tuple<" << RPCTypeNameSequence() << ">"; return Name.data(); } private: static std::mutex NameMutex; static std::string Name; }; template std::mutex RPCTypeName>::NameMutex; template std::string RPCTypeName>::Name; template class RPCTypeName> { public: static const char*getName() { std::lock_guard Lock(NameMutex); if (Name.empty()) raw_string_ostream(Name) << "std::vector<" << RPCTypeName::getName() << ">"; return Name.data(); } private: static std::mutex NameMutex; static std::string Name; }; template std::mutex RPCTypeName>::NameMutex; template std::string RPCTypeName>::Name; /// The SerializationTraits class describes how to serialize and /// deserialize an instance of type T to/from an abstract channel of type /// ChannelT. It also provides a representation of the type's name via the /// getName method. /// /// Specializations of this class should provide the following functions: /// /// @code{.cpp} /// /// static const char* getName(); /// static Error serialize(ChannelT&, const T&); /// static Error deserialize(ChannelT&, T&); /// /// @endcode /// /// The third argument of SerializationTraits is intended to support SFINAE. /// E.g.: /// /// @code{.cpp} /// /// class MyVirtualChannel { ... }; /// /// template /// class SerializationTraits::value /// >::type> { /// public: /// static const char* getName() { ... }; /// } /// /// @endcode template class SerializationTraits; template class SequenceTraits { public: static Error emitSeparator(ChannelT &C) { return Error::success(); } static Error consumeSeparator(ChannelT &C) { return Error::success(); } }; /// Utility class for serializing sequences of values of varying types. /// Specializations of this class contain 'serialize' and 'deserialize' methods /// for the given channel. The ArgTs... list will determine the "over-the-wire" /// types to be serialized. The serialize and deserialize methods take a list /// CArgTs... ("caller arg types") which must be the same length as ArgTs..., /// but may be different types from ArgTs, provided that for each CArgT there /// is a SerializationTraits specialization /// SerializeTraits with methods that can serialize the /// caller argument to over-the-wire value. template class SequenceSerialization; template class SequenceSerialization { public: static Error serialize(ChannelT &C) { return Error::success(); } static Error deserialize(ChannelT &C) { return Error::success(); } }; template class SequenceSerialization { public: template static Error serialize(ChannelT &C, CArgT &&CArg) { return SerializationTraits::type>:: serialize(C, std::forward(CArg)); } template static Error deserialize(ChannelT &C, CArgT &CArg) { return SerializationTraits::deserialize(C, CArg); } }; template class SequenceSerialization { public: template static Error serialize(ChannelT &C, CArgT &&CArg, CArgTs &&... CArgs) { if (auto Err = SerializationTraits::type>:: serialize(C, std::forward(CArg))) return Err; if (auto Err = SequenceTraits::emitSeparator(C)) return Err; return SequenceSerialization:: serialize(C, std::forward(CArgs)...); } template static Error deserialize(ChannelT &C, CArgT &CArg, CArgTs &... CArgs) { if (auto Err = SerializationTraits::deserialize(C, CArg)) return Err; if (auto Err = SequenceTraits::consumeSeparator(C)) return Err; return SequenceSerialization::deserialize(C, CArgs...); } }; template Error serializeSeq(ChannelT &C, ArgTs &&... Args) { return SequenceSerialization::type...>:: serialize(C, std::forward(Args)...); } template Error deserializeSeq(ChannelT &C, ArgTs &... Args) { return SequenceSerialization::deserialize(C, Args...); } template class SerializationTraits { public: using WrappedErrorSerializer = std::function; using WrappedErrorDeserializer = std::function; template static void registerErrorType(std::string Name, SerializeFtor Serialize, DeserializeFtor Deserialize) { assert(!Name.empty() && "The empty string is reserved for the Success value"); const std::string *KeyName = nullptr; { // We're abusing the stability of std::map here: We take a reference to the // key of the deserializers map to save us from duplicating the string in // the serializer. This should be changed to use a stringpool if we switch // to a map type that may move keys in memory. std::lock_guard Lock(DeserializersMutex); auto I = Deserializers.insert(Deserializers.begin(), std::make_pair(std::move(Name), std::move(Deserialize))); KeyName = &I->first; } { assert(KeyName != nullptr && "No keyname pointer"); std::lock_guard Lock(SerializersMutex); // FIXME: Move capture Serialize once we have C++14. Serializers[ErrorInfoT::classID()] = [KeyName, Serialize](ChannelT &C, const ErrorInfoBase &EIB) -> Error { assert(EIB.dynamicClassID() == ErrorInfoT::classID() && "Serializer called for wrong error type"); if (auto Err = serializeSeq(C, *KeyName)) return Err; return Serialize(C, static_cast(EIB)); }; } } static Error serialize(ChannelT &C, Error &&Err) { std::lock_guard Lock(SerializersMutex); if (!Err) return serializeSeq(C, std::string()); return handleErrors(std::move(Err), [&C](const ErrorInfoBase &EIB) { auto SI = Serializers.find(EIB.dynamicClassID()); if (SI == Serializers.end()) return serializeAsStringError(C, EIB); return (SI->second)(C, EIB); }); } static Error deserialize(ChannelT &C, Error &Err) { std::lock_guard Lock(DeserializersMutex); std::string Key; if (auto Err = deserializeSeq(C, Key)) return Err; if (Key.empty()) { ErrorAsOutParameter EAO(&Err); Err = Error::success(); return Error::success(); } auto DI = Deserializers.find(Key); assert(DI != Deserializers.end() && "No deserializer for error type"); return (DI->second)(C, Err); } private: static Error serializeAsStringError(ChannelT &C, const ErrorInfoBase &EIB) { std::string ErrMsg; { raw_string_ostream ErrMsgStream(ErrMsg); EIB.log(ErrMsgStream); } return serialize(C, make_error(std::move(ErrMsg), inconvertibleErrorCode())); } static std::recursive_mutex SerializersMutex; static std::recursive_mutex DeserializersMutex; static std::map Serializers; static std::map Deserializers; }; template std::recursive_mutex SerializationTraits::SerializersMutex; template std::recursive_mutex SerializationTraits::DeserializersMutex; template std::map::WrappedErrorSerializer> SerializationTraits::Serializers; template std::map::WrappedErrorDeserializer> SerializationTraits::Deserializers; /// Registers a serializer and deserializer for the given error type on the /// given channel type. template void registerErrorSerialization(std::string Name, SerializeFtor &&Serialize, DeserializeFtor &&Deserialize) { SerializationTraits::template registerErrorType( std::move(Name), std::forward(Serialize), std::forward(Deserialize)); } /// Registers serialization/deserialization for StringError. template void registerStringError() { static bool AlreadyRegistered = false; if (!AlreadyRegistered) { registerErrorSerialization( "StringError", [](ChannelT &C, const StringError &SE) { return serializeSeq(C, SE.getMessage()); }, [](ChannelT &C, Error &Err) -> Error { ErrorAsOutParameter EAO(&Err); std::string Msg; if (auto E2 = deserializeSeq(C, Msg)) return E2; Err = make_error(std::move(Msg), orcError( OrcErrorCode::UnknownErrorCodeFromRemote)); return Error::success(); }); AlreadyRegistered = true; } } /// SerializationTraits for Expected from an Expected. template class SerializationTraits, Expected> { public: static Error serialize(ChannelT &C, Expected &&ValOrErr) { if (ValOrErr) { if (auto Err = serializeSeq(C, true)) return Err; return SerializationTraits::serialize(C, *ValOrErr); } if (auto Err = serializeSeq(C, false)) return Err; return serializeSeq(C, ValOrErr.takeError()); } static Error deserialize(ChannelT &C, Expected &ValOrErr) { ExpectedAsOutParameter EAO(&ValOrErr); bool HasValue; if (auto Err = deserializeSeq(C, HasValue)) return Err; if (HasValue) return SerializationTraits::deserialize(C, *ValOrErr); Error Err = Error::success(); if (auto E2 = deserializeSeq(C, Err)) return E2; ValOrErr = std::move(Err); return Error::success(); } }; /// SerializationTraits for Expected from a T2. template class SerializationTraits, T2> { public: static Error serialize(ChannelT &C, T2 &&Val) { return serializeSeq(C, Expected(std::forward(Val))); } }; /// SerializationTraits for Expected from an Error. template class SerializationTraits, Error> { public: static Error serialize(ChannelT &C, Error &&Err) { return serializeSeq(C, Expected(std::move(Err))); } }; /// SerializationTraits default specialization for std::pair. template class SerializationTraits> { public: static Error serialize(ChannelT &C, const std::pair &V) { return serializeSeq(C, V.first, V.second); } static Error deserialize(ChannelT &C, std::pair &V) { return deserializeSeq(C, V.first, V.second); } }; /// SerializationTraits default specialization for std::tuple. template class SerializationTraits> { public: /// RPC channel serialization for std::tuple. static Error serialize(ChannelT &C, const std::tuple &V) { return serializeTupleHelper(C, V, llvm::index_sequence_for()); } /// RPC channel deserialization for std::tuple. static Error deserialize(ChannelT &C, std::tuple &V) { return deserializeTupleHelper(C, V, llvm::index_sequence_for()); } private: // Serialization helper for std::tuple. template static Error serializeTupleHelper(ChannelT &C, const std::tuple &V, llvm::index_sequence _) { return serializeSeq(C, std::get(V)...); } // Serialization helper for std::tuple. template static Error deserializeTupleHelper(ChannelT &C, std::tuple &V, llvm::index_sequence _) { return deserializeSeq(C, std::get(V)...); } }; /// SerializationTraits default specialization for std::vector. template class SerializationTraits> { public: /// Serialize a std::vector from std::vector. static Error serialize(ChannelT &C, const std::vector &V) { if (auto Err = serializeSeq(C, static_cast(V.size()))) return Err; for (const auto &E : V) if (auto Err = serializeSeq(C, E)) return Err; return Error::success(); } /// Deserialize a std::vector to a std::vector. static Error deserialize(ChannelT &C, std::vector &V) { uint64_t Count = 0; if (auto Err = deserializeSeq(C, Count)) return Err; V.resize(Count); for (auto &E : V) if (auto Err = deserializeSeq(C, E)) return Err; return Error::success(); } }; } // end namespace rpc } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H