diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt index 7f1438eea4409..8ac8a126dd012 100644 --- a/orc-rt/include/CMakeLists.txt +++ b/orc-rt/include/CMakeLists.txt @@ -15,6 +15,7 @@ set(ORC_RT_HEADERS orc-rt/ResourceManager.h orc-rt/RTTI.h orc-rt/ScopeExit.h + orc-rt/Session.h orc-rt/SimpleNativeMemoryMap.h orc-rt/SimplePackedSerialization.h orc-rt/SPSAllocAction.h diff --git a/orc-rt/include/orc-rt/Session.h b/orc-rt/include/orc-rt/Session.h new file mode 100644 index 0000000000000..c198d374bc849 --- /dev/null +++ b/orc-rt/include/orc-rt/Session.h @@ -0,0 +1,74 @@ +//===-------- Session.h - Session class and related APIs -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Session class and related APIs. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_SESSION_H +#define ORC_RT_SESSION_H + +#include "orc-rt/Error.h" +#include "orc-rt/ResourceManager.h" +#include "orc-rt/move_only_function.h" + +#include + +namespace orc_rt { + +/// Represents an ORC executor Session. +class Session { +public: + using ErrorReporterFn = move_only_function; + using OnShutdownCompleteFn = move_only_function; + + /// Create a session object. The ReportError function will be called to + /// report errors generated while serving JIT'd code, e.g. if a memory + /// management request cannot be fulfilled. (Error's within the JIT'd + /// program are not generally visible to ORC-RT, but can optionally be + /// reported by calling orc_rc_Session_reportError function. + /// + /// Note that entry into the reporter is not synchronized: it may be + /// called from multiple threads concurrently. + Session(ErrorReporterFn ReportError) : ReportError(std::move(ReportError)) {} + + // Sessions are not copyable or moveable. + Session(const Session &) = delete; + Session &operator=(const Session &) = delete; + + ~Session(); + + /// Report an error via the ErrorReporter function. + void reportError(Error Err) { ReportError(std::move(Err)); } + + /// Initiate session shutdown. + /// + /// Runs shutdown on registered resources in reverse order. + void shutdown(OnShutdownCompleteFn OnComplete); + + /// Initiate session shutdown and block until complete. + void waitForShutdown(); + + /// Add a ResourceManager to the session. + void addResourceManager(std::unique_ptr RM) { + std::scoped_lock Lock(M); + ResourceMgrs.push_back(std::move(RM)); + } + +private: + void shutdownNext(OnShutdownCompleteFn OnShutdownComplete, Error Err, + std::vector> RemainingRMs); + + std::mutex M; + ErrorReporterFn ReportError; + std::vector> ResourceMgrs; +}; + +} // namespace orc_rt + +#endif // ORC_RT_SESSION_H diff --git a/orc-rt/lib/executor/CMakeLists.txt b/orc-rt/lib/executor/CMakeLists.txt index 2c709e2b2ca5d..5d83ac19cb719 100644 --- a/orc-rt/lib/executor/CMakeLists.txt +++ b/orc-rt/lib/executor/CMakeLists.txt @@ -3,6 +3,7 @@ set(files AllocAction.cpp ResourceManager.cpp RTTI.cpp + Session.cpp SimpleNativeMemoryMap.cpp ) diff --git a/orc-rt/lib/executor/Session.cpp b/orc-rt/lib/executor/Session.cpp new file mode 100644 index 0000000000000..599bc8705f397 --- /dev/null +++ b/orc-rt/lib/executor/Session.cpp @@ -0,0 +1,60 @@ +//===- Session.cpp --------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Contains the implementation of the Session class and related APIs. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/Session.h" + +#include + +namespace orc_rt { + +Session::~Session() { waitForShutdown(); } + +void Session::shutdown(OnShutdownCompleteFn OnShutdownComplete) { + std::vector> ToShutdown; + + { + std::scoped_lock Lock(M); + std::swap(ResourceMgrs, ToShutdown); + } + + shutdownNext(std::move(OnShutdownComplete), Error::success(), + std::move(ToShutdown)); +} + +void Session::waitForShutdown() { + std::promise P; + auto F = P.get_future(); + + shutdown([P = std::move(P)]() mutable { P.set_value(); }); + + F.wait(); +} + +void Session::shutdownNext( + OnShutdownCompleteFn OnComplete, Error Err, + std::vector> RemainingRMs) { + if (Err) + reportError(std::move(Err)); + + if (RemainingRMs.empty()) + return OnComplete(); + + auto NextRM = std::move(RemainingRMs.back()); + RemainingRMs.pop_back(); + NextRM->shutdown([this, RemainingRMs = std::move(RemainingRMs), + OnComplete = std::move(OnComplete)](Error Err) mutable { + shutdownNext(std::move(OnComplete), std::move(Err), + std::move(RemainingRMs)); + }); +} + +} // namespace orc_rt diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt index 639eaecf16366..2928fc862b8d0 100644 --- a/orc-rt/unittests/CMakeLists.txt +++ b/orc-rt/unittests/CMakeLists.txt @@ -23,6 +23,7 @@ add_orc_rt_unittest(CoreTests MemoryFlagsTest.cpp RTTITest.cpp ScopeExitTest.cpp + SessionTest.cpp SimpleNativeMemoryMapTest.cpp SimplePackedSerializationTest.cpp SPSAllocActionTest.cpp diff --git a/orc-rt/unittests/SessionTest.cpp b/orc-rt/unittests/SessionTest.cpp new file mode 100644 index 0000000000000..7e6084484e227 --- /dev/null +++ b/orc-rt/unittests/SessionTest.cpp @@ -0,0 +1,105 @@ +//===- SessionTest.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Tests for orc-rt's Session.h APIs. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/Session.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +using namespace orc_rt; +using ::testing::Eq; +using ::testing::Optional; + +class MockResourceManager : public ResourceManager { +public: + enum class Op { Detach, Shutdown }; + + static Error alwaysSucceed(Op) { return Error::success(); } + + MockResourceManager(std::optional &DetachOpIdx, + std::optional &ShutdownOpIdx, size_t &OpIdx, + move_only_function GenResult = alwaysSucceed) + : DetachOpIdx(DetachOpIdx), ShutdownOpIdx(ShutdownOpIdx), OpIdx(OpIdx), + GenResult(std::move(GenResult)) {} + + void detach(OnCompleteFn OnComplete) override { + DetachOpIdx = OpIdx++; + OnComplete(GenResult(Op::Detach)); + } + + void shutdown(OnCompleteFn OnComplete) override { + ShutdownOpIdx = OpIdx++; + OnComplete(GenResult(Op::Shutdown)); + } + +private: + std::optional &DetachOpIdx; + std::optional &ShutdownOpIdx; + size_t &OpIdx; + move_only_function GenResult; +}; + +// Non-overloaded version of cantFail: allows easy construction of +// move_only_functionss. +static void noErrors(Error Err) { cantFail(std::move(Err)); } + +TEST(SessionTest, TrivialConstructionAndDestruction) { Session S(noErrors); } + +TEST(SessionTest, ReportError) { + Error E = Error::success(); + cantFail(std::move(E)); // Force error into checked state. + + Session S([&](Error Err) { E = std::move(Err); }); + S.reportError(make_error("foo")); + + if (E) + EXPECT_EQ(toString(std::move(E)), "foo"); + else + ADD_FAILURE() << "Missing error value"; +} + +TEST(SessionTest, SingleResourceManager) { + size_t OpIdx = 0; + std::optional DetachOpIdx; + std::optional ShutdownOpIdx; + + { + Session S(noErrors); + S.addResourceManager(std::make_unique( + DetachOpIdx, ShutdownOpIdx, OpIdx)); + } + + EXPECT_EQ(OpIdx, 1U); + EXPECT_EQ(DetachOpIdx, std::nullopt); + EXPECT_THAT(ShutdownOpIdx, Optional(Eq(0))); +} + +TEST(SessionTest, MultipleResourceManagers) { + size_t OpIdx = 0; + std::optional DetachOpIdx[3]; + std::optional ShutdownOpIdx[3]; + + { + Session S(noErrors); + for (size_t I = 0; I != 3; ++I) + S.addResourceManager(std::make_unique( + DetachOpIdx[I], ShutdownOpIdx[I], OpIdx)); + } + + EXPECT_EQ(OpIdx, 3U); + // Expect shutdown in reverse order. + for (size_t I = 0; I != 3; ++I) { + EXPECT_EQ(DetachOpIdx[I], std::nullopt); + EXPECT_THAT(ShutdownOpIdx[I], Optional(Eq(2 - I))); + } +}