diff --git a/include/llvm/ExecutionEngine/Orc/OrcError.h b/include/llvm/ExecutionEngine/Orc/OrcError.h index 2fe4a5ee058..e85cbe30970 100644 --- a/include/llvm/ExecutionEngine/Orc/OrcError.h +++ b/include/llvm/ExecutionEngine/Orc/OrcError.h @@ -27,26 +27,15 @@ enum class OrcErrorCode : int { RemoteMProtectAddrUnrecognized, RemoteIndirectStubsOwnerDoesNotExist, RemoteIndirectStubsOwnerIdAlreadyInUse, + RPCConnectionClosed, + RPCCouldNotNegotiateFunction, RPCResponseAbandoned, UnexpectedRPCCall, UnexpectedRPCResponse, - UnknownRPCFunction }; std::error_code orcError(OrcErrorCode ErrCode); -class RPCFunctionNotSupported : public ErrorInfo { -public: - static char ID; - - RPCFunctionNotSupported(std::string RPCFunctionSignature); - std::error_code convertToErrorCode() const override; - void log(raw_ostream &OS) const override; - const std::string &getFunctionSignature() const; -private: - std::string RPCFunctionSignature; -}; - } // End namespace orc. } // End namespace llvm. diff --git a/include/llvm/ExecutionEngine/Orc/RPCUtils.h b/include/llvm/ExecutionEngine/Orc/RPCUtils.h index fe7e1ba6ff7..c8b3704b5fc 100644 --- a/include/llvm/ExecutionEngine/Orc/RPCUtils.h +++ b/include/llvm/ExecutionEngine/Orc/RPCUtils.h @@ -32,6 +32,109 @@ namespace llvm { namespace orc { namespace rpc { +/// Base class of all fatal RPC errors (those that necessarily result in the +/// termination of the RPC session). +class RPCFatalError : public ErrorInfo { +public: + static char ID; +}; + +/// RPCConnectionClosed is returned from RPC operations if the RPC connection +/// has already been closed due to either an error or graceful disconnection. +class ConnectionClosed : public ErrorInfo { +public: + static char ID; + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; +}; + +/// BadFunctionCall is returned from handleOne when the remote makes a call with +/// an unrecognized function id. +/// +/// This error is fatal because Orc RPC needs to know how to parse a function +/// call to know where the next call starts, and if it doesn't recognize the +/// function id it cannot parse the call. +template +class BadFunctionCall + : public ErrorInfo, RPCFatalError> { +public: + static char ID; + + BadFunctionCall(FnIdT FnId, SeqNoT SeqNo) + : FnId(std::move(FnId)), SeqNo(std::move(SeqNo)) {} + + std::error_code convertToErrorCode() const override { + return orcError(OrcErrorCode::UnexpectedRPCCall); + } + + void log(raw_ostream &OS) const override { + OS << "Call to invalid RPC function id '" << FnId << "' with " + "sequence number " << SeqNo; + } + +private: + FnIdT FnId; + SeqNoT SeqNo; +}; + +template +char BadFunctionCall::ID = 0; + +/// InvalidSequenceNumberForResponse is returned from handleOne when a response +/// call arrives with a sequence number that doesn't correspond to any in-flight +/// function call. +/// +/// This error is fatal because Orc RPC needs to know how to parse the rest of +/// the response call to know where the next call starts, and if it doesn't have +/// a result parser for this sequence number it can't do that. +template +class InvalidSequenceNumberForResponse + : public ErrorInfo, RPCFatalError> { +public: + static char ID; + + InvalidSequenceNumberForResponse(SeqNoT SeqNo) + : SeqNo(std::move(SeqNo)) {} + + std::error_code convertToErrorCode() const override { + return orcError(OrcErrorCode::UnexpectedRPCCall); + }; + + void log(raw_ostream &OS) const override { + OS << "Response has unknown sequence number " << SeqNo; + } +private: + SeqNoT SeqNo; +}; + +template +char InvalidSequenceNumberForResponse::ID = 0; + +/// This non-fatal error will be passed to asynchronous result handlers in place +/// of a result if the connection goes down before a result returns, or if the +/// function to be called cannot be negotiated with the remote. +class ResponseAbandoned : public ErrorInfo { +public: + static char ID; + + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; +}; + +/// This error is returned if the remote does not have a handler installed for +/// the given RPC function. +class CouldNotNegotiate : public ErrorInfo { +public: + static char ID; + + CouldNotNegotiate(std::string Signature); + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; + const std::string &getSignature() const { return Signature; } +private: + std::string Signature; +}; + template class Function; // RPC Function class. @@ -500,7 +603,7 @@ public: // Create an error instance representing an abandoned response. static Error createAbandonedResponseError() { - return errorCodeToError(orcError(OrcErrorCode::RPCResponseAbandoned)); + return make_error(); } }; @@ -814,12 +917,9 @@ public: if (auto FnIdOrErr = getRemoteFunctionId(LazyAutoNegotiation, false)) FnId = *FnIdOrErr; else { - // This isn't a channel error so we don't want to abandon other pending - // responses, but we still need to run the user handler with an error to - // let them know the call failed. - if (auto Err = Handler(errorCodeToError( - orcError(OrcErrorCode::UnknownRPCFunction)))) - report_fatal_error(std::move(Err)); + // Negotiation failed. Notify the handler then return the negotiate-failed + // error. + cantFail(Handler(make_error())); return FnIdOrErr.takeError(); } @@ -885,7 +985,8 @@ public: return I->second(C, SeqNo); // else: No handler found. Report error to client? - return errorCodeToError(orcError(OrcErrorCode::UnexpectedRPCCall)); + return make_error>(FnId, + SeqNo); } /// Helper for handling setter procedures - this method returns a functor that @@ -995,7 +1096,8 @@ protected: // Unlock the pending results map to prevent recursive lock. Lock.unlock(); abandonPendingResponses(); - return errorCodeToError(orcError(OrcErrorCode::UnexpectedRPCResponse)); + return make_error< + InvalidSequenceNumberForResponse>(SeqNo); } } @@ -1041,7 +1143,7 @@ protected: Impl.template callB(Func::getPrototype())) { RemoteFunctionIds[Func::getPrototype()] = *RemoteIdOrErr; if (*RemoteIdOrErr == getInvalidFunctionId()) - return make_error(Func::getPrototype()); + return make_error(Func::getPrototype()); return *RemoteIdOrErr; } else return RemoteIdOrErr.takeError(); @@ -1049,7 +1151,7 @@ protected: // No key was available in the map and we weren't allowed to try to // negotiate one, so return an unknown function error. - return make_error(Func::getPrototype()); + return make_error(Func::getPrototype()); } using WrappedHandlerFn = std::function; diff --git a/include/llvm/Support/Error.h b/include/llvm/Support/Error.h index 21664d4b715..d5421b97c5f 100644 --- a/include/llvm/Support/Error.h +++ b/include/llvm/Support/Error.h @@ -64,6 +64,12 @@ public: /// using std::error_code. It will be removed in the future. virtual std::error_code convertToErrorCode() const = 0; + // Returns the class ID for this type. + static const void *classID() { return &ID; } + + // Returns the class ID for the dynamic type of this ErrorInfoBase instance. + virtual const void *dynamicClassID() const = 0; + // Check whether this instance is a subclass of the class identified by // ClassID. virtual bool isA(const void *const ClassID) const { @@ -75,9 +81,6 @@ public: return isA(ErrorInfoT::classID()); } - // Returns the class ID for this type. - static const void *classID() { return &ID; } - private: virtual void anchor(); @@ -316,11 +319,14 @@ template Error make_error(ArgTs &&... Args) { template class ErrorInfo : public ParentErrT { public: + + static const void *classID() { return &ThisErrT::ID; } + + const void *dynamicClassID() const override { return &ThisErrT::ID; } + bool isA(const void *const ClassID) const override { return ClassID == classID() || ParentErrT::isA(ClassID); } - - static const void *classID() { return &ThisErrT::ID; } }; /// Special ErrorInfo subclass representing a list of ErrorInfos. @@ -926,6 +932,8 @@ public: void log(raw_ostream &OS) const override; std::error_code convertToErrorCode() const override; + const std::string &getMessage() const { return Msg; } + private: std::string Msg; std::error_code EC; diff --git a/lib/ExecutionEngine/Orc/CMakeLists.txt b/lib/ExecutionEngine/Orc/CMakeLists.txt index 685e882e4a8..f83e002c758 100644 --- a/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_library(LLVMOrcJIT OrcCBindings.cpp OrcError.cpp OrcMCJITReplacement.cpp + RPCUtils.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc diff --git a/lib/ExecutionEngine/Orc/OrcError.cpp b/lib/ExecutionEngine/Orc/OrcError.cpp index dcbbf5f2ae7..c1f228c98cb 100644 --- a/lib/ExecutionEngine/Orc/OrcError.cpp +++ b/lib/ExecutionEngine/Orc/OrcError.cpp @@ -39,14 +39,16 @@ public: return "Remote indirect stubs owner does not exist"; case OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse: return "Remote indirect stubs owner Id already in use"; + case OrcErrorCode::RPCConnectionClosed: + return "RPC connection closed"; + case OrcErrorCode::RPCCouldNotNegotiateFunction: + return "Could not negotiate RPC function"; case OrcErrorCode::RPCResponseAbandoned: return "RPC response abandoned"; case OrcErrorCode::UnexpectedRPCCall: return "Unexpected RPC call"; case OrcErrorCode::UnexpectedRPCResponse: return "Unexpected RPC response"; - case OrcErrorCode::UnknownRPCFunction: - return "Unknown RPC function"; } llvm_unreachable("Unhandled error code"); } @@ -58,27 +60,10 @@ static ManagedStatic OrcErrCat; namespace llvm { namespace orc { -char RPCFunctionNotSupported::ID = 0; - std::error_code orcError(OrcErrorCode ErrCode) { typedef std::underlying_type::type UT; return std::error_code(static_cast(ErrCode), *OrcErrCat); } -RPCFunctionNotSupported::RPCFunctionNotSupported(std::string RPCFunctionSignature) - : RPCFunctionSignature(std::move(RPCFunctionSignature)) {} - -std::error_code RPCFunctionNotSupported::convertToErrorCode() const { - return orcError(OrcErrorCode::UnknownRPCFunction); -} - -void RPCFunctionNotSupported::log(raw_ostream &OS) const { - OS << "Could not negotiate RPC function '" << RPCFunctionSignature << "'"; -} - -const std::string &RPCFunctionNotSupported::getFunctionSignature() const { - return RPCFunctionSignature; -} - } } diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp index 3d46ef88f7c..a84610f5eb4 100644 --- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp @@ -604,10 +604,10 @@ TEST(DummyRPC, TestAPICalls) { { auto Err = DummyCallsAll::negotiate(Client); - EXPECT_EQ(errorToErrorCode(std::move(Err)).value(), - static_cast(OrcErrorCode::UnknownRPCFunction)) - << "Expected 'UnknownRPCFunction' error for attempted negotiate of " + EXPECT_TRUE(Err.isA()) + << "Expected CouldNotNegotiate error for attempted negotiate of " "unsupported function"; + consumeError(std::move(Err)); } ServerThread.join();