diff --git a/CHANGELOG.md b/CHANGELOG.md index f1119aa2..60624355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * The CMake namespace was changed from `roc::` to `hip::` * `AIS_BUILD_EXAMPLES` has been renamed to `AIS_INSTALL_EXAMPLES` * `AIS_USE_SANITIZERS` now also enables the following sanitizers: integer, float-divide-by-zero, local-bounds, vptr, nullability (in addition to address, leak, and undefined). Sanitizers should also now emit usable stack trace info. +* The AIS optimized IO path will automatically fallback to the POSIX IO path if a failure occurs and the compatability mode has not been disabled. ### Removed * The rocFile library has been completely removed and the code is now a part of hipFile. diff --git a/src/amd_detail/CMakeLists.txt b/src/amd_detail/CMakeLists.txt index c264f630..1153b87a 100644 --- a/src/amd_detail/CMakeLists.txt +++ b/src/amd_detail/CMakeLists.txt @@ -7,6 +7,7 @@ set(HIPFILE_SOURCES "${HIPFILE_SRC_COMMON_PATH}/hipfile-common.cpp" async.cpp + backend.cpp backend/asyncop-fallback.cpp backend/memcpy-kernel.hip backend/fallback.cpp diff --git a/src/amd_detail/backend.cpp b/src/amd_detail/backend.cpp new file mode 100644 index 00000000..c9fc5725 --- /dev/null +++ b/src/amd_detail/backend.cpp @@ -0,0 +1,89 @@ +/* Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: MIT + */ + +#include "backend.h" +#include "buffer.h" +#include "file.h" +#include "io.h" + +#include +#include +#include +#include +#include +#include + +using namespace hipFile; + +ssize_t +Backend::io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) +{ + ssize_t nbytes = _io_impl(type, file, buffer, size, file_offset, buffer_offset); + switch (type) { + case (IoType::Read): + update_read_stats(nbytes); + break; + case (IoType::Write): + update_write_stats(nbytes); + break; + default: + break; + } + return nbytes; +} + +ssize_t +BackendWithFallback::io(IoType type, std::shared_ptr file, std::shared_ptr buffer, + size_t size, hoff_t file_offset, hoff_t buffer_offset) +{ + ssize_t nbytes{0}; + try { + nbytes = _io_impl(type, file, buffer, size, file_offset, buffer_offset); + if (nbytes < 0) { + // Typically we should not reach this point. But in case we do, throw + // an exception to use the fallback backend. + throw std::system_error(-static_cast(nbytes), std::generic_category()); + } + } + catch (...) { + std::exception_ptr e_ptr = std::current_exception(); + if (is_fallback_eligible(e_ptr, nbytes, file, buffer, size, file_offset, buffer_offset)) { + nbytes = fallback_backend->io(type, file, buffer, size, file_offset, buffer_offset); + } + else { + throw; + } + return nbytes; + } + switch (type) { + case (IoType::Read): + update_read_stats(nbytes); + break; + case (IoType::Write): + update_write_stats(nbytes); + break; + default: + break; + } + return nbytes; +} + +bool +BackendWithFallback::is_fallback_eligible(std::exception_ptr e_ptr, ssize_t nbytes, + std::shared_ptr file, std::shared_ptr buffer, + size_t size, hoff_t file_offset, hoff_t buffer_offset) const +{ + (void)e_ptr; + (void)nbytes; + return static_cast(fallback_backend) && + fallback_backend->score(file, buffer, size, file_offset, buffer_offset) >= 0; +} + +void +BackendWithFallback::register_fallback_backend(std::shared_ptr backend) noexcept +{ + fallback_backend = backend; +} diff --git a/src/amd_detail/backend.h b/src/amd_detail/backend.h index c2e040df..cfc0d289 100644 --- a/src/amd_detail/backend.h +++ b/src/amd_detail/backend.h @@ -12,6 +12,7 @@ #include "sys.h" #include +#include #include #include #include @@ -51,11 +52,74 @@ struct Backend { /// @param file_offset Offset from the start of the file /// @param buffer_offset Offset from the start of the buffer /// - /// @return Number of bytes transferred, negative on error + /// @return Number of bytes transferred /// /// @throws Hip::RuntimeError Sys::RuntimeError virtual ssize_t io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, - hoff_t file_offset, hoff_t buffer_offset) = 0; + hoff_t file_offset, hoff_t buffer_offset); + + /// @brief Update the read stats for this Backend + /// + /// @param nbytes Number of bytes read + virtual void update_read_stats(ssize_t nbytes) = 0; + + /// @brief Update the write stats for this Backend + /// + /// @param nbytes Number of bytes written + virtual void update_write_stats(ssize_t nbytes) = 0; + +protected: + /// @brief Perform a read or write operation + /// + /// @note Provides a common target across all Backends that provides the + /// implementation for running IO. + /// @param type IO type (read/write) + /// @param file File to read from or write to + /// @param buffer Buffer to write to or read from + /// @param size Number of bytes to transfer + /// @param file_offset Offset from the start of the file + /// @param buffer_offset Offset from the start of the buffer + /// + /// @return Number of bytes transferred + /// + /// @throws Hip::RuntimeError Sys::RuntimeError + virtual ssize_t _io_impl(IoType type, std::shared_ptr file, std::shared_ptr buffer, + size_t size, hoff_t file_offset, hoff_t buffer_offset) = 0; +}; + +// BackendWithFallback allows for an IO to be retried automatically with a +// different Backend in the event of an error. +struct BackendWithFallback : public Backend { + ssize_t io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) override final; + + /// @brief Check if a failed IO operation can be re-issued to the fallback Backend. + /// + /// @param e_ptr exception_ptr to the thrown exception from the failed IO + /// @param nbytes Return value from `_io_impl`, or 0 if an exception was thrown. + /// @param file File to read from or write to + /// @param buffer Buffer to write to or read from + /// @param size Number of bytes to transfer + /// @param file_offset Offset from the start of the file + /// @param buffer_offset Offset from the start of the buffer + /// + /// @note By default, BackendWithFallback checks if a Backend has been + /// registered for retrying an IO, and that fallback backend supports + /// the request. + /// @note The parameters from the original IO request are passed to this function. + /// + /// @return True if this BackendWithFallback can retry the IO, else False. + virtual bool is_fallback_eligible(std::exception_ptr e_ptr, ssize_t nbytes, std::shared_ptr file, + std::shared_ptr buffer, size_t size, hoff_t file_offset, + hoff_t buffer_offset) const; + + /// @brief Register a Backend to retry a failed IO operation. + /// + /// @param backend Backend to retry a failed IO operation. + void register_fallback_backend(std::shared_ptr backend) noexcept; + +protected: + std::shared_ptr fallback_backend; }; } diff --git a/src/amd_detail/backend/fallback.cpp b/src/amd_detail/backend/fallback.cpp index 1d2caa73..2e33e05a 100644 --- a/src/amd_detail/backend/fallback.cpp +++ b/src/amd_detail/backend/fallback.cpp @@ -58,8 +58,33 @@ Fallback::io(IoType type, std::shared_ptr file, std::shared_ptr } ssize_t -Fallback::io(IoType io_type, shared_ptr file, shared_ptr buffer, size_t size, +Fallback::io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, hoff_t file_offset, hoff_t buffer_offset, size_t chunk_size) +{ + ssize_t nbytes = _io_impl(type, file, buffer, size, file_offset, buffer_offset, chunk_size); + switch (type) { + case (IoType::Read): + update_read_stats(nbytes); + break; + case (IoType::Write): + update_write_stats(nbytes); + break; + default: + break; + } + return nbytes; +} + +ssize_t +Fallback::_io_impl(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) +{ + return _io_impl(type, file, buffer, size, file_offset, buffer_offset, DefaultChunkSize); +} + +ssize_t +Fallback::_io_impl(IoType io_type, shared_ptr file, shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset, size_t chunk_size) { size = min(size, hipFile::MAX_RW_COUNT); @@ -115,19 +140,21 @@ Fallback::io(IoType io_type, shared_ptr file, shared_ptr buffer, } } while (static_cast(total_io_bytes) < size); - switch (io_type) { - case IoType::Read: - statsAddFallbackPathRead(static_cast(total_io_bytes)); - break; - case IoType::Write: - statsAddFallbackPathWrite(static_cast(total_io_bytes)); - break; - default: - break; - } return total_io_bytes; } +void +Fallback::update_read_stats(ssize_t nbytes) +{ + statsAddFallbackPathRead(static_cast(nbytes)); +} + +void +Fallback::update_write_stats(ssize_t nbytes) +{ + statsAddFallbackPathWrite(static_cast(nbytes)); +} + void Fallback::async_io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t *size_p, hoff_t *file_offset_p, hoff_t *buffer_offset_p, ssize_t *bytes_transferred_p, diff --git a/src/amd_detail/backend/fallback.h b/src/amd_detail/backend/fallback.h index 14e3b483..d35c068a 100644 --- a/src/amd_detail/backend/fallback.h +++ b/src/amd_detail/backend/fallback.h @@ -34,16 +34,24 @@ struct Fallback : public Backend { ssize_t io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, hoff_t file_offset, hoff_t buffer_offset) override; + void update_read_stats(ssize_t nbytes) override; + + void update_write_stats(ssize_t nbytes) override; + void async_io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t *size_p, hoff_t *file_offset_p, hoff_t *buffer_offset_p, ssize_t *bytes_transferred_p, std::shared_ptr stream); // Once we can import gtest.h and make test suites or test friends everything // below here should be made protected. - // protected: - ssize_t io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, hoff_t file_offset, hoff_t buffer_offset, size_t chunk_size); + +protected: + ssize_t _io_impl(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) override; + ssize_t _io_impl(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset, size_t chunk_size); }; } diff --git a/src/amd_detail/backend/fastpath.cpp b/src/amd_detail/backend/fastpath.cpp index aba587c8..fa06eeac 100644 --- a/src/amd_detail/backend/fastpath.cpp +++ b/src/amd_detail/backend/fastpath.cpp @@ -154,9 +154,21 @@ Fastpath::score(shared_ptr file, shared_ptr buffer, size_t size, return accept_io ? 100 : -1; } +void +Fastpath::update_read_stats(ssize_t nbytes) +{ + statsAddFastPathRead(static_cast(nbytes)); +} + +void +Fastpath::update_write_stats(ssize_t nbytes) +{ + statsAddFastPathWrite(static_cast(nbytes)); +} + ssize_t -Fastpath::io(IoType type, shared_ptr file, shared_ptr buffer, size_t size, hoff_t file_offset, - hoff_t buffer_offset) +Fastpath::_io_impl(IoType type, shared_ptr file, shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) { void *devptr{reinterpret_cast(reinterpret_cast(buffer->getBuffer()) + buffer_offset)}; hipAmdFileHandle_t handle{}; @@ -184,15 +196,5 @@ Fastpath::io(IoType type, shared_ptr file, shared_ptr buffer, si default: throw std::runtime_error("Invalid IoType"); } - switch (type) { - case IoType::Read: - statsAddFastPathRead(nbytes); - break; - case IoType::Write: - statsAddFastPathWrite(nbytes); - break; - default: - break; - } return static_cast(nbytes); } diff --git a/src/amd_detail/backend/fastpath.h b/src/amd_detail/backend/fastpath.h index 61f0915e..87e72433 100644 --- a/src/amd_detail/backend/fastpath.h +++ b/src/amd_detail/backend/fastpath.h @@ -9,6 +9,7 @@ #include "hipfile.h" #include +#include #include namespace hipFile { @@ -23,14 +24,17 @@ enum class IoType; namespace hipFile { -struct Fastpath : public Backend { +struct Fastpath : public BackendWithFallback { virtual ~Fastpath() override = default; - int score(std::shared_ptr file, std::shared_ptr buffer, size_t size, hoff_t file_offset, - hoff_t buffer_offset) const override; + int score(std::shared_ptr file, std::shared_ptr buffer, size_t size, hoff_t file_offset, + hoff_t buffer_offset) const override; + void update_read_stats(ssize_t nbytes) override; + void update_write_stats(ssize_t nbytes) override; - ssize_t io(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, - hoff_t file_offset, hoff_t buffer_offset) override; +protected: + ssize_t _io_impl(IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset) override; }; } diff --git a/src/amd_detail/state.cpp b/src/amd_detail/state.cpp index fd4aacfa..19cb29aa 100644 --- a/src/amd_detail/state.cpp +++ b/src/amd_detail/state.cpp @@ -275,11 +275,18 @@ std::vector> DriverState::getBackends() const { static bool once = [&]() { - if (Context::get()->fastpath()) { - backends.emplace_back(new Fastpath{}); - } + std::shared_ptr fallback_backend; if (Context::get()->fallback()) { - backends.emplace_back(new Fallback{}); + fallback_backend = std::make_shared(); + backends.push_back(fallback_backend); + } + + if (Context::get()->fastpath()) { + auto new_backend = std::make_shared(); + if (fallback_backend) { + new_backend->register_fallback_backend(fallback_backend); + } + backends.push_back(new_backend); } return true; }(); diff --git a/test/amd_detail/CMakeLists.txt b/test/amd_detail/CMakeLists.txt index c44514f5..e42f17a9 100644 --- a/test/amd_detail/CMakeLists.txt +++ b/test/amd_detail/CMakeLists.txt @@ -12,6 +12,7 @@ set(SHARED_SOURCE_FILES set(TEST_SOURCE_FILES async.cpp + backend.cpp batch/batch.cpp configuration.cpp context.cpp diff --git a/test/amd_detail/backend.cpp b/test/amd_detail/backend.cpp new file mode 100644 index 00000000..d0c00d0f --- /dev/null +++ b/test/amd_detail/backend.cpp @@ -0,0 +1,103 @@ +/* Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: MIT + */ + +#include "io.h" +#include "hipfile-test.h" +#include "hipfile-warnings.h" +#include "mbackend.h" +#include "mbuffer.h" +#include "mfile.h" + +#include +#include +#include + +using namespace hipFile; + +using ::testing::AnyNumber; +using ::testing::Return; +using ::testing::StrictMock; +using ::testing::Throw; + +// Put tests inside the macros to suppress the global constructor +// warnings +HIPFILE_WARN_NO_GLOBAL_CTOR_OFF + +struct DummyBackendWithFallback : public MBackendWithFallback {}; + +struct DummyFallbackBackend : MBackend {}; + +struct HipFileBackendWithFallback : public HipFileUnopened, ::testing::WithParamInterface { + std::shared_ptr> mock_buffer; + std::shared_ptr> mock_file; + + std::shared_ptr> default_backend; + std::shared_ptr> fallback_backend; + + IoType io_type; + ssize_t successful_io_size = 0x1234; + + void SetUp() override + { + mock_buffer = std::make_shared>(); + mock_file = std::make_shared>(); + + default_backend = std::make_shared>(); + EXPECT_CALL(*default_backend, _io_impl).Times(AnyNumber()); + EXPECT_CALL(*default_backend, update_read_stats).Times(AnyNumber()); + EXPECT_CALL(*default_backend, update_write_stats).Times(AnyNumber()); + + fallback_backend = std::make_shared>(); + EXPECT_CALL(*fallback_backend, io).Times(AnyNumber()); + EXPECT_CALL(*fallback_backend, score).WillRepeatedly(Return(0)); + + io_type = GetParam(); + } + + HipFileBackendWithFallback() + { + } +}; + +TEST_P(HipFileBackendWithFallback, IOSuccess) +{ + EXPECT_CALL(*default_backend, _io_impl).WillOnce(Return(successful_io_size)); + + ssize_t nbytes = default_backend->io(io_type, mock_file, mock_buffer, 0, 0, 0); + + ASSERT_EQ(nbytes, successful_io_size); +} + +TEST_P(HipFileBackendWithFallback, IOFailureNoFallback) +{ + EXPECT_CALL(*default_backend, _io_impl).WillOnce(Throw(std::runtime_error("IO failure"))); + + EXPECT_THROW(default_backend->io(io_type, mock_file, mock_buffer, 0, 0, 0), std::runtime_error); +} + +TEST_P(HipFileBackendWithFallback, IOFailureWithIneligibleRetry) +{ + // The Backend has registered a fallback, but has determined that the + // IO error should not be retried. + default_backend->register_fallback_backend(fallback_backend); + EXPECT_CALL(*default_backend, _io_impl).WillOnce(Throw(std::runtime_error("IO failure"))); + EXPECT_CALL(*fallback_backend, score).WillOnce(Return(-1)); + + EXPECT_THROW(default_backend->io(io_type, mock_file, mock_buffer, 0, 0, 0), std::runtime_error); +} + +TEST_P(HipFileBackendWithFallback, IOFailureWithGoodFallback) +{ + default_backend->register_fallback_backend(fallback_backend); + EXPECT_CALL(*default_backend, _io_impl).WillOnce(Throw(std::runtime_error("IO failure"))); + EXPECT_CALL(*fallback_backend, io).WillOnce(Return(successful_io_size)); + + ssize_t nbytes = default_backend->io(io_type, mock_file, mock_buffer, 0, 0, 0); + ASSERT_EQ(nbytes, successful_io_size); +} + +INSTANTIATE_TEST_SUITE_P(, HipFileBackendWithFallback, ::testing::Values(IoType::Read, IoType::Write)); + +HIPFILE_WARN_NO_GLOBAL_CTOR_ON diff --git a/test/amd_detail/fastpath.cpp b/test/amd_detail/fastpath.cpp index 258f29d0..f0d4c061 100644 --- a/test/amd_detail/fastpath.cpp +++ b/test/amd_detail/fastpath.cpp @@ -3,6 +3,7 @@ * SPDX-License-Identifier: MIT */ +#include "backend/fallback.h" #include "backend/fastpath.h" #include "hip.h" #include "hipfile.h" @@ -12,10 +13,12 @@ #include "mbuffer.h" #include "mfile.h" #include "mhip.h" +#include "msys.h" #include #include #include +#include #include #include #include @@ -25,6 +28,7 @@ #include #include #include +#include using namespace hipFile; using namespace testing; @@ -555,4 +559,128 @@ TEST_P(FastpathIoParam, IoSizeIsTruncatedToMaxRWCount) INSTANTIATE_TEST_SUITE_P(FastpathTest, FastpathIoParam, Values(IoType::Read, IoType::Write)); +struct FastpathIoParamWithFallback : public FastpathTestBase, + public TestWithParam> { + inline IoType _get_param_io_type() const + { + return std::get<0>(GetParam()); + } + + inline const std::exception_ptr _get_param_exc_ptr() const + { + return std::get<1>(GetParam()); + } +}; + +// The Fastpath can throw a few different kinds of derived std::runtime_errors. +TEST_P(FastpathIoParamWithFallback, IntegrationRunWithFallback) +{ + StrictMock mhip; + StrictMock msys; + + auto fallback_backend = std::make_shared>(); + auto fastpath_backend = std::make_shared>(); + fastpath_backend->register_fallback_backend(fallback_backend); + + const int DEFAULT_BUFFERED_FD = DEFAULT_UNBUFFERED_FD.value() + 1; + + // Called by both Fastpath and Fallback + EXPECT_CALL(*mbuffer, getBuffer).WillRepeatedly(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getLength).Times(2).WillRepeatedly(Return(DEFAULT_BUFFER_LENGTH)); + // Called only by Fastpath + EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + // Called only by Fallback + EXPECT_CALL(*mbuffer, getType).WillOnce(Return(hipMemoryTypeDevice)); + EXPECT_CALL(*mfile, getBufferedFd).WillRepeatedly(Return(DEFAULT_BUFFERED_FD)); + EXPECT_CALL(mhip, hipMemcpy).WillRepeatedly(Return()); + EXPECT_CALL(msys, mmap).WillOnce(Return(reinterpret_cast(0x12345678))); + EXPECT_CALL(msys, munmap).WillOnce(Return()); + switch (_get_param_io_type()) { + case IoType::Read: + // Called by Fastpath + EXPECT_CALL(mhip, hipAmdFileRead).WillOnce(Rethrow(_get_param_exc_ptr())); + // Called by Fallback + EXPECT_CALL(msys, pread).WillRepeatedly(ReturnArg<2>()); + break; + case IoType::Write: + // Called by Fastpath + EXPECT_CALL(mhip, hipAmdFileWrite).WillOnce(Rethrow(_get_param_exc_ptr())); + // Called by Fallback + EXPECT_CALL(mhip, hipStreamSynchronize).WillRepeatedly(Return()); + EXPECT_CALL(msys, fdatasync).WillRepeatedly(Return()); + EXPECT_CALL(msys, pwrite).WillRepeatedly(ReturnArg<2>()); + break; + default: + FAIL() << "Invalid IoType"; + } + + ssize_t num_bytes = fastpath_backend->io(_get_param_io_type(), mfile, mbuffer, DEFAULT_IO_SIZE, 0, 0); + ASSERT_EQ(num_bytes, DEFAULT_IO_SIZE); +} + +// If the fallback backend rejects the IO, the original exception from +// Fastpath should be raised. +TEST_P(FastpathIoParamWithFallback, IntegrationFallbackRejectsIO) +{ + StrictMock mhip; + StrictMock msys; + + auto fallback_backend = std::make_shared>(); + auto fastpath_backend = std::make_shared>(); + fastpath_backend->register_fallback_backend(fallback_backend); + + // Called only by Fastpath + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); + EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + // Called only by Fallback - should fail the score() check. + EXPECT_CALL(*mbuffer, getType).WillOnce(Return(hipMemoryTypeHost)); + switch (_get_param_io_type()) { + case IoType::Read: + // Called by Fastpath + EXPECT_CALL(mhip, hipAmdFileRead).WillOnce(Rethrow(_get_param_exc_ptr())); + break; + case IoType::Write: + // Called by Fastpath + EXPECT_CALL(mhip, hipAmdFileWrite).WillOnce(Rethrow(_get_param_exc_ptr())); + break; + default: + FAIL() << "Invalid IoType"; + } + + // Have to rethrow the exception_ptr to be able to access the exception + // This looks ugly, but is better than the alternative of trying to preserve the + // exception type when setting Throw(*std::shared_ptr>). + try { + std::rethrow_exception(_get_param_exc_ptr()); + } + catch (const std::exception &expected_exc) { + // Can't use EXPECT_THROW due to the thrown exception type only being known at runtime + try { + fastpath_backend->io(_get_param_io_type(), mfile, mbuffer, DEFAULT_IO_SIZE, 0, 0); + FAIL() << "io() was expected to throw, but it returned normally"; + } + catch (const std::exception &actual_exc) { + // Verify that the propagated exception has the same dynamic type and message + // as the one stored in the original std::exception_ptr, without relying on + // pointer identity of the underlying exception object. + ASSERT_EQ(typeid(expected_exc), typeid(actual_exc)); + ASSERT_STREQ(expected_exc.what(), actual_exc.what()); + } + catch (...) { + FAIL() << "io() threw something other than a std::exception"; + } + } +} + +// Using std::exception_ptr is more straightforward here than storing a pointer +// to a derived std::exception type, which would require careful handling when +// setting expectations. Note that Throw() does not accept std::exception_ptr, +// but the public (though undocumented) Rethrow() action does support it. +INSTANTIATE_TEST_SUITE_P( + FastpathTest, FastpathIoParamWithFallback, + Combine(Values(IoType::Read, IoType::Write), + Values(std::make_exception_ptr(Hip::RuntimeError(hipErrorNoDevice)), + std::make_exception_ptr(std::system_error(make_error_code(errc::no_such_device)))))); + HIPFILE_WARN_NO_GLOBAL_CTOR_ON diff --git a/test/amd_detail/mbackend.h b/test/amd_detail/mbackend.h index f4c395df..d544e0b2 100644 --- a/test/amd_detail/mbackend.h +++ b/test/amd_detail/mbackend.h @@ -18,6 +18,22 @@ struct MBackend : Backend { (hipFile::IoType type, std::shared_ptr, std::shared_ptr, size_t, hoff_t, hoff_t), (override)); + MOCK_METHOD(void, update_read_stats, (ssize_t nbytes), (override)); + MOCK_METHOD(void, update_write_stats, (ssize_t nbytes), (override)); + MOCK_METHOD(ssize_t, _io_impl, + (hipFile::IoType type, std::shared_ptr, std::shared_ptr, size_t, hoff_t, + hoff_t), + (override)); }; +struct MBackendWithFallback : BackendWithFallback { + MOCK_METHOD(int, score, (std::shared_ptr, std::shared_ptr, size_t, hoff_t, hoff_t), + (const, override)); + MOCK_METHOD(void, update_read_stats, (ssize_t nbytes), (override)); + MOCK_METHOD(void, update_write_stats, (ssize_t nbytes), (override)); + MOCK_METHOD(ssize_t, _io_impl, + (IoType type, std::shared_ptr file, std::shared_ptr buffer, size_t size, + hoff_t file_offset, hoff_t buffer_offset), + (override)); +}; }