From 7e312f1829a5aede297c881cc3c682744e442ef6 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Mon, 18 Dec 2023 15:29:42 -0800 Subject: [PATCH 01/26] Remove own file operation implementation - Depends on C++17 filesystem --- CMakeLists.txt | 46 +++---- cmake/check_cxx_filesystem_library.cmake | 25 ++++ cmake/include_cxx_filesystem_library.cmake | 40 ------ docs/readthedocs/basics/getting_started.md | 17 +-- include/metall/detail/file.hpp | 147 --------------------- tutorial/hands_on/README.md | 15 +-- tutorial/nvmw21/README.md | 15 +-- 7 files changed, 50 insertions(+), 255 deletions(-) create mode 100644 cmake/check_cxx_filesystem_library.cmake delete mode 100644 cmake/include_cxx_filesystem_library.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b818fa77..c0fb10f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,13 @@ include(FetchContent) # -------------------------------------------------------------------------------- # # CMake policy # -------------------------------------------------------------------------------- # -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") cmake_policy(SET CMP0077 NEW) -endif() +endif () -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") cmake_policy(SET CMP0135 NEW) -endif() +endif () # -------------------------------------------------------------------------------- # # Metall general configuration @@ -113,11 +113,11 @@ endif () # -------------------------------------------------------------------------------- # if (INSTALL_HEADER_ONLY) message(WARNING "INSTALL_HEADER_ONLY option has been replaced with JUST_INSTALL_METALL_HEADER.") -endif() +endif () if (JUST_INSTALL_METALL_HEADER) return() -endif() +endif () # -------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------- # @@ -138,7 +138,7 @@ endif () # -------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------- # -# Executables +# Set up for building executables # -------------------------------------------------------------------------------- # # Requirements for GCC @@ -151,9 +151,9 @@ if (NOT RUN_BUILD_AND_TEST_WITH_CI) endif () # ---------- Metall Macros ---------- # -foreach(X ${COMPILER_DEFS}) +foreach (X ${COMPILER_DEFS}) message(STATUS "Metall compile definition: ${X}") -endforeach() +endforeach () # ---------- CMAKE_BUILD_TYPE ---------- # @@ -168,12 +168,8 @@ find_package(Threads REQUIRED) # ---------- filesystem ---------- # -include(include_cxx_filesystem_library) -include_cxx_filesystem_library() - -# Xcode 11 Beta Release Notes -# Clang now supports the C++17 library for iOS 13, macOS 10.15, watchOS 6, and tvOS 13. (50988273) -# https://developer.apple.com/documentation/xcode_release_notes/xcode_11_beta_release_notes?language=objc +include(check_cxx_filesystem_library) +check_cxx_filesystem_library() # ---------- UMap ---------- # @@ -192,7 +188,7 @@ set(Boost_NO_BOOST_CMAKE ON) find_package(Boost 1.64 QUIET) if (NOT Boost_FOUND) FetchContent_Declare(Boost - URL https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2) + URL https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2) FetchContent_GetProperties(Boost) if (NOT Boost_POPULATED) FetchContent_Populate(Boost) @@ -246,20 +242,18 @@ function(common_setup_for_metall_executable name) # -------------------- # ----- Compile Definitions ----- # - foreach(X ${COMPILER_DEFS}) - target_compile_definitions(${name} PRIVATE ${X}) - endforeach() + foreach (X ${COMPILER_DEFS}) + target_compile_definitions(${name} PRIVATE ${X}) + endforeach () # -------------------- # ----- CXX17 Filesystem Lib----- # - # include_cxx_filesystem_library module must be executed first - if (FOUND_CXX17_FILESYSTEM_LIB) - if (REQUIRE_LIB_STDCXX_FS) + # GNU compilers prior to 9.1 requires linking with stdc++fs + if (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) target_link_libraries(${name} PRIVATE stdc++fs) - endif() - elseif() - target_compile_definitions(${name} PRIVATE "METALL_DISABLE_CXX17_FILESYSTEM_LIB") - endif() + endif () + endif () # -------------------- # ----- Umap----- # diff --git a/cmake/check_cxx_filesystem_library.cmake b/cmake/check_cxx_filesystem_library.cmake new file mode 100644 index 00000000..88eb5ad7 --- /dev/null +++ b/cmake/check_cxx_filesystem_library.cmake @@ -0,0 +1,25 @@ +# Checks if the C++17 library is available. +function(check_cxx_filesystem_library) + + set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) + set(REQUIRE_LIB_STDCXX_FS FALSE PARENT_SCOPE) + + # Check if C++17 header files are available + # If not, uses our own implementation. + include(CheckIncludeFileCXX) + CHECK_INCLUDE_FILE_CXX(filesystem FOUND_FILESYSTEM_HEADER) + if (NOT FOUND_FILESYSTEM_HEADER) + message(FATAL_ERROR "Cannot find the C++17 library.") + endif () + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang + if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") # macOS + include(get_macos_version) + get_macos_version() # Get macOS version + message(VERBOSE "Detected macOS version ${MACOS_VERSION}") + if (MACOS_VERSION VERSION_LESS 10.15) # macOS < 10.15 + message(FATAL_ERROR "macOS >= 10.15 is required to use the C++17 library.") + endif () + endif () + endif () +endfunction() \ No newline at end of file diff --git a/cmake/include_cxx_filesystem_library.cmake b/cmake/include_cxx_filesystem_library.cmake deleted file mode 100644 index a54ca061..00000000 --- a/cmake/include_cxx_filesystem_library.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# Find the correct option to link the C++17 library. -# If it is not available, uses own implementation. -function(include_cxx_filesystem_library) - - set(FOUND_CXX17_FILESYSTEM_LIB TRUE PARENT_SCOPE) - set(REQUIRE_LIB_STDCXX_FS FALSE PARENT_SCOPE) - - # Check if C++17 header files are available - # If not, uses our own implementation. - include(CheckIncludeFileCXX) - CHECK_INCLUDE_FILE_CXX(filesystem FOUND_FILESYSTEM_HEADER) - if (NOT FOUND_FILESYSTEM_HEADER) - message(STATUS "Cannot find the C++17 library.") - set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) - return() - endif () - - # Find the correct option to link the C++17 library - # GCC - # Any platform: stdc++fs - # LLVM - # Assumes LLVM >= 9.0 and libc++ >= 7.0. - # Clang + macOS >= 10.15: nothing special is required to use - # Clang + macOS < 10.15: uses our own implementation - # Clang + the other OS: nothing special is required to use - if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) # GCC - set(REQUIRE_LIB_STDCXX_FS TRUE PARENT_SCOPE) - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang - if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") # macOS - include(get_macos_version) - get_macos_version() # Get macOS version - message(VERBOSE "macOS version ${MACOS_VERSION}") - if (MACOS_VERSION VERSION_LESS 10.15) # macOS < 10.15 - message(STATUS "macOS >= 10.15 is required to use the C++17 library.") - set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) - endif () - endif () - endif () - -endfunction() \ No newline at end of file diff --git a/docs/readthedocs/basics/getting_started.md b/docs/readthedocs/basics/getting_started.md index 45965228..79c8a629 100644 --- a/docs/readthedocs/basics/getting_started.md +++ b/docs/readthedocs/basics/getting_started.md @@ -69,22 +69,11 @@ g++ -std=c++17 your_program.cpp -lstdc++fs -I${BOOST_ROOT}/include -I${METALL_RO Clang (or Apple clang) could be used instead of GCC to build Metall. However, we haven't tested it intensively. +To run on macOS, Metall requires macOS >= 10.15. + Also, Boost C++ Libraries 1.69 or more may be required if one wants to build Metall with Clang + CUDA. -**On macOS >= 10.15 or Linux** - ```bash -# Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB -``` +``` \ No newline at end of file diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index f10a999e..b44aeb0d 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -28,37 +28,15 @@ #include #include #include - -#ifdef __has_include - -// Check if the Filesystem library is available or disabled by the user -#if __has_include() && !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) #include -#else -#ifdef METALL_VERBOSE_SYSTEM_SUPPORT_WARNING -#warning "The Filesystem library is not available or disabled by the user." -#endif -#endif - -#else // __has_include is not defined - -#ifdef METALL_VERBOSE_SYSTEM_SUPPORT_WARNING -#warning \ - "__has_include is not defined, consequently disable the Filesystem library." -#endif // METALL_VERBOSE_SYSTEM_SUPPORT_WARNING - -#endif // #ifdef __has_include #include namespace metall::mtlldetail { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) namespace { namespace fs = std::filesystem; } -#endif inline bool os_close(const int fd) { if (::close(fd) == -1) { @@ -91,8 +69,6 @@ inline bool fsync(const std::string &path) { } inline bool fsync_recursive(const std::string &path) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) fs::path p(path); p = fs::canonical(p); while (true) { @@ -105,23 +81,6 @@ inline bool fsync_recursive(const std::string &path) { p = p.parent_path(); } return true; -#else - char *abs = ::realpath(path.c_str(), NULL); - if (!abs) return false; - char *ref = abs; - while (true) { - if (!fsync(std::string(abs))) { - ::free(ref); - return false; - } - if (::strcmp(abs, "/") == 0) { - break; - } - abs = ::dirname(abs); - } - ::free(ref); - return true; -#endif } inline bool extend_file_size_manually(const int fd, const off_t offset, @@ -229,8 +188,6 @@ inline bool create_file(const std::string &file_path) { return fsync_recursive(file_path); } -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) /// \brief Creates directories recursively. /// \return Returns true if the directory was created or already exists. /// Otherwise, returns false. @@ -269,16 +226,6 @@ inline bool create_directory(const std::string &dir_path) { return success; } -#else -/// \brief Creates directories recursively. -/// \return Returns true if the directory was created or already exists, returns -/// true. Otherwise, returns false. -inline bool create_directory(const std::string &dir_path) { - std::string mkdir_command("mkdir -p " + dir_path); - const int status = std::system(mkdir_command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); -} -#endif inline ssize_t get_file_size(const std::string &file_path) { std::ifstream ifs(file_path, std::ifstream::binary | std::ifstream::ate); @@ -309,17 +256,10 @@ inline ssize_t get_actual_file_size(const std::string &file_path) { /// \return Upon successful completion, returns true; otherwise, false is /// returned. If the file or directory does not exist, true is returned. inline bool remove_file(const std::string &path) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) std::filesystem::path p(path); std::error_code ec; [[maybe_unused]] const auto num_removed = std::filesystem::remove_all(p, ec); return !ec; -#else - std::string rm_command("rm -rf " + path); - const int status = std::system(rm_command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); -#endif } inline bool free_file_space([[maybe_unused]] const int fd, @@ -343,8 +283,6 @@ inline bool free_file_space([[maybe_unused]] const int fd, namespace file_copy_detail { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) inline bool copy_file_dense(const std::string &source_path, const std::string &destination_path) { bool success = true; @@ -369,72 +307,6 @@ inline bool copy_file_dense(const std::string &source_path, return success; } -#else - -inline bool copy_file_dense(const std::string &source_path, - const std::string &destination_path) { - { - const ssize_t source_file_size = get_file_size(source_path); - const ssize_t actual_source_file_size = get_actual_file_size(source_path); - if (source_file_size == -1 || actual_source_file_size == -1) { - return false; - } - - // If the source file is empty, just create the destination file and done. - if (source_file_size == 0 || actual_source_file_size == 0) { - create_file(destination_path); - return true; - } - } - - { - std::ifstream source(source_path); - if (!source.is_open()) { - std::stringstream ss; - ss << "Cannot open: " << source_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - - std::ofstream destination(destination_path); - if (!destination.is_open()) { - std::stringstream ss; - ss << "Cannot open: " << destination_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - - destination << source.rdbuf(); - if (!destination) { - std::stringstream ss; - ss << "Something happened in the ofstream: " << destination_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - - destination.close(); - - if (!metall::mtlldetail::fsync(destination_path)) { - return false; - } - } - - { - // Sanity check - const ssize_t s1 = get_file_size(source_path); - const ssize_t s2 = get_file_size(destination_path); - if (s1 < 0 || s1 != s2) { - std::stringstream ss; - ss << "Something wrong in file sizes: " << s1 << " " << s2; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - } - return true; -} - -#endif - #ifdef __linux__ inline bool copy_file_sparse_linux(const std::string &source_path, const std::string &destination_path) { @@ -483,9 +355,6 @@ inline bool copy_file(const std::string &source_path, /// long as the operation does not fail). Returns false on error. inline bool get_regular_file_names(const std::string &dir_path, std::vector *file_list) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) - if (!directory_exist(dir_path)) { return false; } @@ -504,22 +373,6 @@ inline bool get_regular_file_names(const std::string &dir_path, } return true; - -#else - DIR *d = ::opendir(dir_path.c_str()); - if (!d) { - return false; - } - - for (dirent *dir; (dir = ::readdir(d)) != nullptr;) { - if (dir->d_type == DT_REG) { - file_list->push_back(dir->d_name); - } - } - ::closedir(d); - - return true; -#endif } /// \brief Copy files in a directory. diff --git a/tutorial/hands_on/README.md b/tutorial/hands_on/README.md index a520303b..58243064 100644 --- a/tutorial/hands_on/README.md +++ b/tutorial/hands_on/README.md @@ -117,22 +117,9 @@ g++ -std=c++17 [tutorial_program.cpp] -lstdc++fs -I../../include -I${BOOST_ROOT} Clang (or Apple clang) could be used instead of GCC to build Metall. However, please note that we haven't tested it intensively. - - -**On macOS >= 10.15 or Linux** +To run on macOS, Metall requires macOS >= 10.15. ```bash # Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB ``` \ No newline at end of file diff --git a/tutorial/nvmw21/README.md b/tutorial/nvmw21/README.md index df098202..c71ec21d 100644 --- a/tutorial/nvmw21/README.md +++ b/tutorial/nvmw21/README.md @@ -67,22 +67,9 @@ g++ -std=c++17 [tutorial_program.cpp] -lstdc++fs -I../../include -I${BOOST_ROOT} Clang (or Apple clang) could be used instead of GCC to build Metall. However, please note that we haven't tested it intensively. - - -**On macOS >= 10.15 or Linux** +To run on macOS, Metall requires macOS >= 10.15. ```bash # Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB ``` \ No newline at end of file From 46694460d138f5a77150f14df9f2f85406551b93 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Mon, 18 Dec 2023 18:59:22 -0800 Subject: [PATCH 02/26] Use std::filesystem::path --- bench/bfs/run_bfs_bench_metall.cpp | 2 +- bench/bfs/run_bfs_bench_metall_multiple.cpp | 2 +- .../run_simple_allocation_bench_metall.cpp | 2 +- include/metall/basic_manager.hpp | 45 +-- include/metall/detail/file.hpp | 50 +-- include/metall/detail/file_clone.hpp | 36 ++- include/metall/detail/mmap.hpp | 6 +- include/metall/detail/ptree.hpp | 8 +- .../kernel/attributed_object_directory.hpp | 10 +- include/metall/kernel/bin_directory.hpp | 6 +- include/metall/kernel/chunk_directory.hpp | 8 +- include/metall/kernel/manager_kernel.hpp | 43 +-- include/metall/kernel/manager_kernel_impl.ipp | 36 ++- include/metall/kernel/segment_allocator.hpp | 26 +- src/datastore_ls.cpp | 4 +- src/mpi_datastore_ls.cpp | 6 +- test/container/concurrent_map_test.cpp | 4 +- .../fallback_allocator_adaptor_test.cpp | 4 +- test/container/json/json_value.cpp | 24 +- test/container/stl_allocator_test.cpp | 50 +-- .../attributed_object_directory_test.cpp | 6 +- test/kernel/bin_directory_test.cpp | 6 +- test/kernel/chunk_directory_test.cpp | 6 +- test/kernel/manager_multithread_test.cpp | 6 +- test/kernel/manager_test.cpp | 302 +++++++++--------- test/kernel/multimanager_test.cpp | 16 +- .../kernel/object_attribute_accessor_test.cpp | 20 +- test/kernel/snapshot_test.cpp | 29 +- test/test_utility.hpp | 25 +- 29 files changed, 410 insertions(+), 378 deletions(-) diff --git a/bench/bfs/run_bfs_bench_metall.cpp b/bench/bfs/run_bfs_bench_metall.cpp index ccf3b0b4..a4587060 100644 --- a/bench/bfs/run_bfs_bench_metall.cpp +++ b/bench/bfs/run_bfs_bench_metall.cpp @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { // metall::logger::set_log_level(metall::logger::level::verbose); metall::manager manager(metall::open_read_only, - option.graph_file_name_list[0].c_str()); + option.graph_file_name_list[0]); auto adj_list = manager.find(option.graph_key_name.c_str()).first; diff --git a/bench/bfs/run_bfs_bench_metall_multiple.cpp b/bench/bfs/run_bfs_bench_metall_multiple.cpp index 6fa7e2d3..fd27f6a2 100644 --- a/bench/bfs/run_bfs_bench_metall_multiple.cpp +++ b/bench/bfs/run_bfs_bench_metall_multiple.cpp @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { std::vector managers; for (const auto &file_name : option.graph_file_name_list) { managers.emplace_back( - new metall::manager(metall::open_read_only, file_name.c_str())); + new metall::manager(metall::open_read_only, file_name)); } auto adj_list = adjacency_list_type(option.graph_key_name, managers.begin(), diff --git a/bench/simple_alloc/run_simple_allocation_bench_metall.cpp b/bench/simple_alloc/run_simple_allocation_bench_metall.cpp index 1b9d5249..ff3c0246 100644 --- a/bench/simple_alloc/run_simple_allocation_bench_metall.cpp +++ b/bench/simple_alloc/run_simple_allocation_bench_metall.cpp @@ -13,7 +13,7 @@ int main(int argc, char *argv[]) { const auto option = simple_alloc_bench::parse_option(argc, argv); { - metall::manager manager(metall::create_only, option.datastore_path.c_str()); + metall::manager manager(metall::create_only, option.datastore_path); simple_alloc_bench::run_bench(option, manager.get_allocator()); } metall::manager::remove(option.datastore_path.c_str()); diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index fb18ca9f..5487bbed 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -7,7 +7,9 @@ #define METALL_BASIC_MANAGER_HPP #include +#include #include +#include #include #include @@ -116,7 +118,7 @@ class basic_manager { /// \brief Opens an existing data store. /// \param base_path Path to a data store. - basic_manager(open_only_t, const char *base_path) noexcept { + basic_manager(open_only_t, const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open(base_path); @@ -130,7 +132,8 @@ class basic_manager { /// \brief Opens an existing data store with the read only mode. /// Write accesses will cause segmentation fault. /// \param base_path Path to a data store. - basic_manager(open_read_only_t, const char *base_path) noexcept { + basic_manager(open_read_only_t, + const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open_read_only(base_path); @@ -143,7 +146,8 @@ class basic_manager { /// \brief Creates a new data store (an existing data store will be /// overwritten). \param base_path Path to create a data store. - basic_manager(create_only_t, const char *base_path) noexcept { + basic_manager(create_only_t, + const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->create(base_path); @@ -157,7 +161,7 @@ class basic_manager { /// \brief Creates a new data store (an existing data store will be /// overwritten). \param base_path Path to create a data store. \param /// capacity Maximum total allocation size. - basic_manager(create_only_t, const char *base_path, + basic_manager(create_only_t, const std::filesystem::path &base_path, const size_type capacity) noexcept { try { m_kernel = std::make_unique(); @@ -940,7 +944,8 @@ class basic_manager { /// if it is available. \param num_max_copy_threads The maximum number of copy /// threads to use. If <= 0 is given, the value is automatically determined. /// \return Returns true on success; other false. - bool snapshot(const char_type *destination_dir_path, const bool clone = true, + bool snapshot(const std::filesystem::path &destination_dir_path, + const bool clone = true, const int num_max_copy_threads = 0) noexcept { if (!check_sanity()) { return false; @@ -968,8 +973,8 @@ class basic_manager { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns true; other false. - static bool copy(const char_type *source_dir_path, - const char_type *destination_dir_path, + static bool copy(const std::filesystem::path &source_dir_path, + const std::filesystem::path &destination_dir_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { @@ -995,8 +1000,8 @@ class basic_manager { /// If <= 0 is given, the value is automatically determined. /// \return Returns an object of std::future. /// If succeeded, its get() returns true; other false. - static auto copy_async(const char_type *source_dir_path, - const char_type *destination_dir_path, + static auto copy_async(const std::filesystem::path &source_dir_path, + const std::filesystem::path &destination_dir_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { @@ -1015,7 +1020,7 @@ class basic_manager { /// /// \param dir_path Path to a data store to remove. \return If /// succeeded, returns true; other false. - static bool remove(const char_type *dir_path) noexcept { + static bool remove(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::remove(dir_path); } catch (...) { @@ -1032,7 +1037,8 @@ class basic_manager { /// \param dir_path Path to a data store to remove. /// \return Returns an object of std::future. /// If succeeded, its get() returns true; other false - static std::future remove_async(const char_type *dir_path) noexcept { + static std::future remove_async( + const std::filesystem::path &dir_path) noexcept { try { return std::async(std::launch::async, remove, dir_path); } catch (...) { @@ -1054,7 +1060,7 @@ class basic_manager { /// \param dir_path Path to a data store. /// \return Returns true if it exists and is consistent; otherwise, returns /// false. - static bool consistent(const char_type *dir_path) noexcept { + static bool consistent(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::consistent(dir_path); } catch (...) { @@ -1086,7 +1092,7 @@ class basic_manager { /// /// \param dir_path Path to a data store. /// \return UUID in the std::string format; returns an empty string on error. - static std::string get_uuid(const char_type *dir_path) noexcept { + static std::string get_uuid(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::get_uuid(dir_path); } catch (...) { @@ -1118,7 +1124,7 @@ class basic_manager { /// /// \param dir_path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char_type *dir_path) noexcept { + static version_type get_version(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::get_version(dir_path); } catch (...) { @@ -1161,7 +1167,7 @@ class basic_manager { /// \param dir_path Path to a data store. \param description An std::string /// object that holds a description. \return Returns true on success; /// otherwise, false. - static bool set_description(const char *dir_path, + static bool set_description(const std::filesystem::path &dir_path, const std::string &description) noexcept { try { return manager_kernel_type::set_description(dir_path, description); @@ -1203,7 +1209,7 @@ class basic_manager { /// to an std::string object to store a description if it exists. \return /// Returns true on success; returns false on error. Trying to get a /// non-existent description is not considered as an error. - static bool get_description(const char *dir_path, + static bool get_description(const std::filesystem::path &dir_path, std::string *description) noexcept { try { return manager_kernel_type::get_description(dir_path, description); @@ -1222,7 +1228,7 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an instance /// of named_object_attribute_accessor_type. static named_object_attribute_accessor_type access_named_object_attribute( - const char *dir_path) noexcept { + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_named_object_attribute(dir_path); } catch (...) { @@ -1239,7 +1245,7 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an instance /// of unique_object_attribute_accessor_type. static unique_object_attribute_accessor_type access_unique_object_attribute( - const char *dir_path) noexcept { + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_unique_object_attribute(dir_path); } catch (...) { @@ -1256,7 +1262,8 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an /// instance of anonymous_object_attribute_accessor_type. static anonymous_object_attribute_accessor_type - access_anonymous_object_attribute(const char *dir_path) noexcept { + access_anonymous_object_attribute( + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_anonymous_object_attribute(dir_path); } catch (...) { diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index b44aeb0d..c528ee83 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -54,7 +54,7 @@ inline bool os_fsync(const int fd) { return true; } -inline bool fsync(const std::string &path) { +inline bool fsync(const fs::path &path) { const int fd = ::open(path.c_str(), O_RDONLY); if (fd == -1) { logger::perror(logger::level::error, __FILE__, __LINE__, "open"); @@ -68,7 +68,7 @@ inline bool fsync(const std::string &path) { return ret; } -inline bool fsync_recursive(const std::string &path) { +inline bool fsync_recursive(const fs::path &path) { fs::path p(path); p = fs::canonical(p); while (true) { @@ -135,8 +135,7 @@ inline bool extend_file_size(const int fd, const size_t file_size, return ret; } -inline bool extend_file_size(const std::string &file_path, - const size_t file_size, +inline bool extend_file_size(const fs::path &file_path, const size_t file_size, const bool fill_with_zero = false) { const int fd = ::open(file_path.c_str(), O_RDWR); if (fd == -1) { @@ -152,22 +151,23 @@ inline bool extend_file_size(const std::string &file_path, /// \brief Check if a file, any kinds of file including directory, exists /// \warning This implementation could return a wrong result due to metadata -/// cache on NFS. The following code could fail: if (mpi_rank == 1) -/// file_exist(path); // NFS creates metadata cache mpi_barrier(); if (mpi_rank -/// == 0) create_directory(path); mpi_barrier(); if (mpi_rank == 1) +/// cache on NFS. The following code could fail: +/// if (mpi_rank == 1) +/// file_exist(path); // NFS creates metadata cache +/// mpi_barrier(); +/// if (mpi_rank == 0) +/// create_directory(path); +/// mpi_barrier(); +/// if (mpi_rank == 1) /// assert(file_exist(path)); // Could fail due to the cached metadata. -inline bool file_exist(const std::string &file_name) { - std::string fixed_string(file_name); - while (fixed_string.back() == '/') { - fixed_string.pop_back(); - } - return (::access(fixed_string.c_str(), F_OK) == 0); +inline bool file_exist(const fs::path &file_name) { + return fs::exists(file_name); } /// \brief Check if a directory exists /// \warning This implementation could return a wrong result due to metadata /// cache on NFS. -inline bool directory_exist(const std::string &dir_path) { +inline bool directory_exist(const fs::path &dir_path) { struct stat stat_buf; if (::stat(dir_path.c_str(), &stat_buf) == -1) { return false; @@ -175,7 +175,7 @@ inline bool directory_exist(const std::string &dir_path) { return S_ISDIR(stat_buf.st_mode); } -inline bool create_file(const std::string &file_path) { +inline bool create_file(const fs::path &file_path) { const int fd = ::open(file_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd == -1) { @@ -191,8 +191,8 @@ inline bool create_file(const std::string &file_path) { /// \brief Creates directories recursively. /// \return Returns true if the directory was created or already exists. /// Otherwise, returns false. -inline bool create_directory(const std::string &dir_path) { - std::string fixed_string = dir_path; +inline bool create_directory(const fs::path &dir_path) { + fs::path fixed_string = dir_path; // MEMO: GCC bug 87846 (fixed in v8.3) // "Calling std::filesystem::create_directories with a path with a trailing // separator (e.g. "./a/b/") does not create any directory." @@ -227,7 +227,7 @@ inline bool create_directory(const std::string &dir_path) { return success; } -inline ssize_t get_file_size(const std::string &file_path) { +inline ssize_t get_file_size(const fs::path &file_path) { std::ifstream ifs(file_path, std::ifstream::binary | std::ifstream::ate); ssize_t size = ifs.tellg(); if (size == -1) { @@ -242,10 +242,10 @@ inline ssize_t get_file_size(const std::string &file_path) { /// \brief /// Note that, according to GCC, /// the file system may use some blocks for internal record keeping -inline ssize_t get_actual_file_size(const std::string &file_path) { +inline ssize_t get_actual_file_size(const fs::path &file_path) { struct stat stat_buf; if (::stat(file_path.c_str(), &stat_buf) != 0) { - std::string s("stat (" + file_path + ")"); + std::string s("stat (" + file_path.string() + ")"); logger::perror(logger::level::error, __FILE__, __LINE__, s.c_str()); return -1; } @@ -255,7 +255,7 @@ inline ssize_t get_actual_file_size(const std::string &file_path) { /// \brief Remove a file or directory /// \return Upon successful completion, returns true; otherwise, false is /// returned. If the file or directory does not exist, true is returned. -inline bool remove_file(const std::string &path) { +inline bool remove_file(const fs::path &path) { std::filesystem::path p(path); std::error_code ec; [[maybe_unused]] const auto num_removed = std::filesystem::remove_all(p, ec); @@ -283,8 +283,8 @@ inline bool free_file_space([[maybe_unused]] const int fd, namespace file_copy_detail { -inline bool copy_file_dense(const std::string &source_path, - const std::string &destination_path) { +inline bool copy_file_dense(const fs::path &source_path, + const fs::path &destination_path) { bool success = true; try { if (!fs::copy_file(source_path, destination_path, @@ -308,8 +308,8 @@ inline bool copy_file_dense(const std::string &source_path, } #ifdef __linux__ -inline bool copy_file_sparse_linux(const std::string &source_path, - const std::string &destination_path) { +inline bool copy_file_sparse_linux(const fs::path &source_path, + const fs::path &destination_path) { std::string command("cp --sparse=auto " + source_path + " " + destination_path); const int status = std::system(command.c_str()); diff --git a/include/metall/detail/file_clone.hpp b/include/metall/detail/file_clone.hpp index f4cdf8df..81d3d071 100644 --- a/include/metall/detail/file_clone.hpp +++ b/include/metall/detail/file_clone.hpp @@ -23,21 +23,26 @@ namespace metall::mtlldetail { +namespace { +namespace fs = std::filesystem; +} + namespace file_clone_detail { #ifdef __linux__ -inline bool clone_file_linux(const std::string &source_path, - const std::string &destination_path) { - std::string command("cp --reflink=auto -R " + source_path + " " + - destination_path); +inline bool clone_file_linux(const fs::path &source_path, + const fs::path &destination_path) { + std::string command("cp --reflink=auto -R " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); return (status != -1) && !!(WIFEXITED(status)); } #endif #ifdef __APPLE__ -inline bool clone_file_macos(const std::string &source_path, - const std::string &destination_path) { - std::string command("cp -cR " + source_path + " " + destination_path); +inline bool clone_file_macos(const fs::path &source_path, + const fs::path &destination_path) { + std::string command("cp -cR " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); return (status != -1) && !!(WIFEXITED(status)); } @@ -48,21 +53,21 @@ inline bool clone_file_macos(const std::string &source_path, /// normally. \param source_path A path to the file to be cloned. \param /// destination_path A path to copy to. \return On success, returns true. On /// error, returns false. -inline bool clone_file(const std::string &source_path, - const std::string &destination_path) { +inline bool clone_file(const fs::path &source_path, + const fs::path &destination_path) { bool ret = false; #if defined(__linux__) ret = file_clone_detail::clone_file_linux(source_path, destination_path); if (!ret) { - std::string s("On Linux, Failed to clone " + source_path + " to " + - destination_path); + std::string s("On Linux, Failed to clone " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #elif defined(__APPLE__) ret = file_clone_detail::clone_file_macos(source_path, destination_path); if (!ret) { - std::string s("On MacOS, Failed to clone " + source_path + " to " + - destination_path); + std::string s("On MacOS, Failed to clone " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #else @@ -73,7 +78,8 @@ inline bool clone_file(const std::string &source_path, "Use normal copy instead of clone"); ret = copy_file(source_path, destination_path); // Copy normally if (!ret) { - std::string s("Failed to copy " + source_path + " to " + destination_path); + std::string s("Failed to copy " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #endif @@ -93,7 +99,7 @@ inline bool clone_file(const std::string &source_path, /// If <= 0 is given, it is automatically determined. /// \return On success, returns true. On error, returns false. inline bool clone_files_in_directory_in_parallel( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads) { return copy_files_in_directory_in_parallel_helper( source_dir_path, destination_dir_path, max_num_threads, clone_file); diff --git a/include/metall/detail/mmap.hpp b/include/metall/detail/mmap.hpp index 8f14e7ed..39dac965 100644 --- a/include/metall/detail/mmap.hpp +++ b/include/metall/detail/mmap.hpp @@ -98,7 +98,7 @@ inline void *map_anonymous_write_mode(void *const addr, const size_t length, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_read_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDONLY); @@ -147,7 +147,7 @@ inline void *map_file_write_mode(const int fd, void *const addr, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_write_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDWR); @@ -197,7 +197,7 @@ inline void *map_file_write_private_mode(const int fd, void *const addr, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_write_private_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDWR); diff --git a/include/metall/detail/ptree.hpp b/include/metall/detail/ptree.hpp index e547fb7c..13ef5e74 100644 --- a/include/metall/detail/ptree.hpp +++ b/include/metall/detail/ptree.hpp @@ -6,6 +6,9 @@ #ifndef METALL_DETAIL_UTILITY_PTREE_HPP #define METALL_DETAIL_UTILITY_PTREE_HPP +#include +#include + #include #include @@ -14,6 +17,7 @@ namespace metall::mtlldetail::ptree { namespace { +namespace fs = std::filesystem; namespace bptree = boost::property_tree; } @@ -117,7 +121,7 @@ inline bool push_back(const node_type &child, node_type *parent) { return true; } -inline bool read_json(const std::string &file_name, node_type *root) { +inline bool read_json(const fs::path &file_name, node_type *root) { try { bptree::read_json(file_name, *root); } catch (const bptree::json_parser_error &e) { @@ -127,7 +131,7 @@ inline bool read_json(const std::string &file_name, node_type *root) { return true; } -inline bool write_json(const node_type &root, const std::string &file_name) { +inline bool write_json(const node_type &root, const fs::path &file_name) { try { bptree::write_json(file_name, root); } catch (const bptree::json_parser_error &e) { diff --git a/include/metall/kernel/attributed_object_directory.hpp b/include/metall/kernel/attributed_object_directory.hpp index 3d85d5b4..83075b75 100644 --- a/include/metall/kernel/attributed_object_directory.hpp +++ b/include/metall/kernel/attributed_object_directory.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; namespace json = metall::mtlldetail::ptree; } // namespace @@ -429,7 +431,7 @@ class attributed_object_directory { /// \brief /// \param path - bool serialize(const char *const path) const noexcept { + bool serialize(const fs::path &path) const noexcept { try { return priv_serialize_throw(path); } catch (...) { @@ -440,7 +442,7 @@ class attributed_object_directory { /// \brief /// \param path - bool deserialize(const char *const path) noexcept { + bool deserialize(const fs::path &path) noexcept { try { return priv_deserialize_throw(path); } catch (...) { @@ -507,7 +509,7 @@ class attributed_object_directory { return true; } - bool priv_serialize_throw(const char *const path) const { + bool priv_serialize_throw(const fs::path &path) const { if (!good()) { return false; } @@ -546,7 +548,7 @@ class attributed_object_directory { return true; } - bool priv_deserialize_throw(const char *const path) { + bool priv_deserialize_throw(const fs::path &path) { if (!good()) { return false; } diff --git a/include/metall/kernel/bin_directory.hpp b/include/metall/kernel/bin_directory.hpp index e3b8bb29..f96f2d58 100644 --- a/include/metall/kernel/bin_directory.hpp +++ b/include/metall/kernel/bin_directory.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,7 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; } @@ -195,7 +197,7 @@ class bin_directory { /// \brief /// \param path - bool serialize(const char *path) const { + bool serialize(const fs::path &path) const { std::ofstream ofs(path); if (!ofs.is_open()) { std::stringstream ss; @@ -224,7 +226,7 @@ class bin_directory { /// \brief /// \param path - bool deserialize(const char *path) { + bool deserialize(const fs::path &path) { std::ifstream ifs(path); if (!ifs.is_open()) { std::stringstream ss; diff --git a/include/metall/kernel/chunk_directory.hpp b/include/metall/kernel/chunk_directory.hpp index d5174232..0cc70e8e 100644 --- a/include/metall/kernel/chunk_directory.hpp +++ b/include/metall/kernel/chunk_directory.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,8 +23,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace /// \brief Chunk directory class. /// Chunk directory is a table that stores information about chunks. @@ -294,7 +296,7 @@ class chunk_directory { /// \brief /// \param path - bool serialize(const char *path) const { + bool serialize(const fs::path &path) const { std::ofstream ofs(path); if (!ofs.is_open()) { std::stringstream ss; @@ -358,7 +360,7 @@ class chunk_directory { /// \brief /// \param path /// \return - bool deserialize(const char *path) { + bool deserialize(const fs::path &path) { std::ifstream ifs(path); if (!ifs.is_open()) { std::stringstream ss; diff --git a/include/metall/kernel/manager_kernel.hpp b/include/metall/kernel/manager_kernel.hpp index c7eb3614..3280b9e1 100644 --- a/include/metall/kernel/manager_kernel.hpp +++ b/include/metall/kernel/manager_kernel.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -53,8 +54,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace template class manager_kernel { @@ -102,8 +104,7 @@ class manager_kernel { #ifndef METALL_SEGMENT_BLOCK_SIZE #error "METALL_SEGMENT_BLOCK_SIZE is not defined." #endif - static constexpr size_type k_initial_segment_size = - METALL_SEGMENT_BLOCK_SIZE; + static constexpr size_type k_initial_segment_size = METALL_SEGMENT_BLOCK_SIZE; static_assert(k_initial_segment_size <= k_default_vm_reserve_size, "k_initial_segment_size must be <= k_default_vm_reserve_size"); static_assert(k_chunk_size <= k_initial_segment_size, @@ -195,7 +196,7 @@ class manager_kernel { /// \param base_dir_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool create(const char *base_dir_path, + bool create(const fs::path &base_dir_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore @@ -203,14 +204,14 @@ class manager_kernel { /// \param base_dir_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool open(const char *base_dir_path, + bool open(const fs::path &base_dir_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore with read only /// Expect to be called by a single thread /// \param base_dir_path /// \return Returns true if success; otherwise, returns false - bool open_read_only(const char *base_dir_path); + bool open_read_only(const fs::path &base_dir_path); /// \brief Expect to be called by a single thread void close(); @@ -372,7 +373,7 @@ class manager_kernel { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false - bool snapshot(const char *destination_dir_path, const bool clone, + bool snapshot(const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Copies a data store synchronously, keeping the same UUID. @@ -382,8 +383,8 @@ class manager_kernel { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false. - static bool copy(const char *source_dir_path, - const char *destination_dir_path, const bool clone, + static bool copy(const fs::path &source_dir_path, + const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Copies a data store asynchronously, keeping the same UUID. @@ -394,27 +395,27 @@ class manager_kernel { /// If <= 0 is given, the value is automatically determined. /// \return Returns an object of std::future. /// If succeeded, its get() returns True; other false. - static std::future copy_async(const char *source_dir_path, - const char *destination_dir_path, + static std::future copy_async(const fs::path &source_dir_path, + const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Remove a data store synchronously /// \param base_dir_path /// \return If succeeded, returns True; other false - static bool remove(const char *base_dir_path); + static bool remove(const fs::path &base_dir_path); /// \brief Remove a data store asynchronously /// \param base_dir_path /// \return Returns an object of std::future /// If succeeded, its get() returns True; other false - static std::future remove_async(const char *base_dir_path); + static std::future remove_async(const fs::path &base_dir_path); /// \brief Check if the backing data store is consistent, /// i.e. it was closed properly. /// \param dir_path /// \return Return true if it is consistent; otherwise, returns false. - static bool consistent(const char *dir_path); + static bool consistent(const fs::path &dir_path); /// \brief Returns the UUID of the backing data store. /// \return Returns UUID in std::string; returns an empty string on error. @@ -423,7 +424,7 @@ class manager_kernel { /// \brief Returns the UUID of the backing data store. /// \param dir_path Path to a data store. /// \return Returns UUID in std::string; returns an empty string on error. - static std::string get_uuid(const char *dir_path); + static std::string get_uuid(const fs::path &dir_path); /// \brief Gets the version number of the backing data store. /// \return Returns a version number; returns 0 on error. @@ -432,7 +433,7 @@ class manager_kernel { /// \brief Gets the version number of the backing data store. /// \param dir_path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char *dir_path); + static version_type get_version(const fs::path &dir_path); /// \brief Gets a description from a file. /// \param description A pointer to a string buffer. @@ -443,7 +444,7 @@ class manager_kernel { /// \param base_dir_path Path to a data store. /// \param description A pointer to a string buffer. /// \return Returns false on error. - static bool get_description(const std::string &base_dir_path, + static bool get_description(const fs::path &base_dir_path, std::string *description); /// \brief Sets a description to a file. @@ -550,9 +551,9 @@ class manager_kernel { bool priv_allocate_segment_header(void *addr); bool priv_deallocate_segment_header(); - bool priv_open(const char *base_dir_path, bool read_only, + bool priv_open(const fs::path &base_dir_path, bool read_only, size_type vm_reserve_size_request = 0); - bool priv_create(const char *base_dir_path, size_type vm_reserve_size); + bool priv_create(const fs::path &base_dir_path, size_type vm_reserve_size); // ---------- For serializing/deserializing ---------- // bool priv_serialize_management_data(); @@ -560,8 +561,8 @@ class manager_kernel { // ---------- snapshot ---------- // /// \brief Takes a snapshot. The snapshot has a different UUID. - bool priv_snapshot(const char *destination_base_dir_path, const bool clone, - const int num_max_copy_threads); + bool priv_snapshot(const fs::path &destination_base_dir_path, + const bool clone, const int num_max_copy_threads); // ---------- File operations ---------- // /// \brief Copies all backing files using reflink if possible diff --git a/include/metall/kernel/manager_kernel_impl.ipp b/include/metall/kernel/manager_kernel_impl.ipp index 2d9f7641..6bcb20ee 100644 --- a/include/metall/kernel/manager_kernel_impl.ipp +++ b/include/metall/kernel/manager_kernel_impl.ipp @@ -39,20 +39,20 @@ manager_kernel::~manager_kernel() noexcept { // Public methods // -------------------- // template -bool manager_kernel::create(const char *base_dir_path, +bool manager_kernel::create(const fs::path &base_dir_path, const size_type vm_reserve_size) { return m_good = priv_create(base_dir_path, vm_reserve_size); } template bool manager_kernel::open_read_only( - const char *base_dir_path) { + const fs::path &base_dir_path) { return m_good = priv_open(base_dir_path, true, 0); } template bool manager_kernel::open( - const char *base_dir_path, const size_type vm_reserve_size_request) { + const fs::path &base_dir_path, const size_type vm_reserve_size_request) { return m_good = priv_open(base_dir_path, false, vm_reserve_size_request); } @@ -448,40 +448,41 @@ manager_kernel::get_segment_size() const { template bool manager_kernel::snapshot( - const char *destination_base_dir_path, const bool clone, + const fs::path &destination_base_dir_path, const bool clone, const int num_max_copy_threads) { return priv_snapshot(destination_base_dir_path, clone, num_max_copy_threads); } template bool manager_kernel::copy( - const char *source_base_dir_path, const char *destination_base_dir_path, - const bool clone, const int num_max_copy_threads) { + const fs::path &source_base_dir_path, + const fs::path &destination_base_dir_path, const bool clone, + const int num_max_copy_threads) { return priv_copy_data_store(source_base_dir_path, destination_base_dir_path, clone, num_max_copy_threads); } template std::future manager_kernel::copy_async( - const char *source_dir_path, const char *destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads) { return std::async(std::launch::async, copy, source_dir_path, destination_dir_path, clone, num_max_copy_threads); } template -bool manager_kernel::remove(const char *base_dir_path) { +bool manager_kernel::remove(const fs::path &base_dir_path) { return priv_remove_data_store(base_dir_path); } template std::future manager_kernel::remove_async( - const char *base_dir_path) { + const fs::path &base_dir_path) { return std::async(std::launch::async, remove, base_dir_path); } template -bool manager_kernel::consistent(const char *dir_path) { +bool manager_kernel::consistent(const fs::path &dir_path) { return priv_consistent(dir_path); } @@ -491,7 +492,8 @@ std::string manager_kernel::get_uuid() const { } template -std::string manager_kernel::get_uuid(const char *dir_path) { +std::string manager_kernel::get_uuid( + const fs::path &dir_path) { json_store meta_data; if (!priv_read_management_metadata(dir_path, &meta_data)) { std::stringstream ss; @@ -509,7 +511,7 @@ version_type manager_kernel::get_version() const { template version_type manager_kernel::get_version( - const char *dir_path) { + const fs::path &dir_path) { json_store meta_data; if (!priv_read_management_metadata(dir_path, &meta_data)) { std::stringstream ss; @@ -523,7 +525,7 @@ version_type manager_kernel::get_version( template bool manager_kernel::get_description( - const std::string &base_dir_path, std::string *description) { + const fs::path &base_dir_path, std::string *description) { return priv_read_description(base_dir_path, description); } @@ -633,7 +635,7 @@ bool manager_kernel::priv_init_datastore_directory( } // Remove existing directory to certainly create a new data store - if (!remove(base_dir_path.c_str())) { + if (!remove(base_dir_path)) { std::string s("Failed to remove a directory: " + base_dir_path); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; @@ -944,7 +946,7 @@ void manager_kernel::priv_destruct_and_free_memory( template bool manager_kernel::priv_open( - const char *base_dir_path, const bool read_only, + const fs::path &base_dir_path, const bool read_only, const size_type vm_reserve_size_request) { if (!priv_validate_runtime_configuration()) { return false; @@ -1021,7 +1023,7 @@ bool manager_kernel::priv_open( template bool manager_kernel::priv_create( - const char *base_dir_path, const size_type vm_reserve_size) { + const fs::path &base_dir_path, const size_type vm_reserve_size) { if (!priv_validate_runtime_configuration()) { return false; } @@ -1159,7 +1161,7 @@ bool manager_kernel::priv_deserialize_management_data() { // ---------- snapshot ---------- // template bool manager_kernel::priv_snapshot( - const char *destination_base_dir_path, const bool clone, + const fs::path &destination_base_dir_path, const bool clone, const int num_max_copy_threads) { priv_sanity_check(); m_segment_storage.sync(true); diff --git a/include/metall/kernel/segment_allocator.hpp b/include/metall/kernel/segment_allocator.hpp index 7e601cea..0e53f1c8 100644 --- a/include/metall/kernel/segment_allocator.hpp +++ b/include/metall/kernel/segment_allocator.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -40,8 +41,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace template +#include + #include int main(int argc, char *argv[]) { @@ -12,7 +14,7 @@ int main(int argc, char *argv[]) { std::abort(); } - const std::string datastore_path = argv[1]; + const std::filesystem::path datastore_path = argv[1]; metall::utility::ls_named_object(datastore_path); std::cout << std::endl; diff --git a/src/mpi_datastore_ls.cpp b/src/mpi_datastore_ls.cpp index 57b9dd98..9ff1a43a 100644 --- a/src/mpi_datastore_ls.cpp +++ b/src/mpi_datastore_ls.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -14,14 +16,14 @@ int main(int argc, char *argv[]) { std::abort(); } - const std::string datastore_path = argv[1]; + const std::filesystem::path datastore_path = argv[1]; const int mpi_rank = (argc < 3) ? 0 : std::stoi(argv[2]); const auto local_datastore_path = metall::utility::mpi_datastore::make_local_dir_path(datastore_path, mpi_rank); - if (!metall::manager::consistent(local_datastore_path.c_str())) { + if (!metall::manager::consistent(local_datastore_path)) { std::cerr << "Inconsistent datastore or invalid datastore path" << std::endl; std::abort(); diff --git a/test/container/concurrent_map_test.cpp b/test/container/concurrent_map_test.cpp index 16fc3943..a14d61a3 100644 --- a/test/container/concurrent_map_test.cpp +++ b/test/container/concurrent_map_test.cpp @@ -5,6 +5,8 @@ #include "gtest/gtest.h" +#include + #include #include #include @@ -130,7 +132,7 @@ TEST(ConcurrentMapTest, Persistence) { metall::container::concurrent_map, std::hash, allocator_type, 2>; - const std::string file_path(test_utility::make_test_path()); + const std::filesystem::path file_path(test_utility::make_test_path()); test_utility::create_test_dir(); metall::mtlldetail::remove_file(file_path); diff --git a/test/container/fallback_allocator_adaptor_test.cpp b/test/container/fallback_allocator_adaptor_test.cpp index 34d0e6b3..4a1443a9 100644 --- a/test/container/fallback_allocator_adaptor_test.cpp +++ b/test/container/fallback_allocator_adaptor_test.cpp @@ -199,7 +199,7 @@ TEST(FallbackAllocatorAdaptorTest, PersistentConstructFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.construct("int")(10); @@ -212,7 +212,7 @@ TEST(FallbackAllocatorAdaptorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); const auto ret1 = manager.find("int"); ASSERT_NE(ret1.first, nullptr); diff --git a/test/container/json/json_value.cpp b/test/container/json/json_value.cpp index d2d6c279..f04ab36b 100644 --- a/test/container/json/json_value.cpp +++ b/test/container/json/json_value.cpp @@ -365,10 +365,10 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::create_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); auto *jv_copy = manager_copy.construct("jv")(manager_copy.get_allocator()); @@ -380,7 +380,7 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::open_read_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); const auto *const jv_copy = manager_copy.find("jv").first; check_json_string(*jv_copy); } @@ -391,10 +391,10 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); metall::manager manager_copy( metall::create_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); auto json_src = mj::parse(json_string, manager_src.get_allocator()); // Construct a new one from another instance that was allocated by another // allocator @@ -406,7 +406,7 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::open_read_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); const auto *const jv_copy = manager_copy.find("jv").first; check_json_string(*jv_copy); } @@ -420,10 +420,10 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::create_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); auto *jv_move = manager_move.construct("jv")(manager_move.get_allocator()); @@ -434,7 +434,7 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::open_read_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); const auto *const jv_move = manager_move.find("jv").first; check_json_string(*jv_move); } @@ -445,10 +445,10 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); metall::manager manager_move( metall::create_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); // Construct a new one from another instance that was allocated by another // allocator auto *jv_move = manager_move.construct("jv")( @@ -460,7 +460,7 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::open_read_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); const auto *const jv_move = manager_move.find("jv").first; check_json_string(*jv_move); } diff --git a/test/container/stl_allocator_test.cpp b/test/container/stl_allocator_test.cpp index 7935186a..af2685a2 100644 --- a/test/container/stl_allocator_test.cpp +++ b/test/container/stl_allocator_test.cpp @@ -4,19 +4,25 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) #include "gtest/gtest.h" + #include #include +#include + #include #include #include #include #include "../test_utility.hpp" +namespace { +namespace fs = std::filesystem; + template using alloc_type = metall::manager::allocator_type; -const std::string &dir_path() { - const static std::string path(test_utility::make_test_path()); +const fs::path &dir_path() { + const static fs::path path(test_utility::make_test_path()); return path; } @@ -100,8 +106,7 @@ TEST(StlAllocatorTest, Types) { }; using alloc_t = alloc_type; - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 24UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 24UL); alloc_t alloc = manager.get_allocator(); { @@ -130,7 +135,7 @@ TEST(StlAllocatorTest, Types) { } TEST(StlAllocatorTest, Exception) { - metall::manager manager(metall::create_only, dir_path().c_str(), 1UL << 24UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 24UL); alloc_type allocator = manager.get_allocator(); @@ -142,16 +147,16 @@ TEST(StlAllocatorTest, Exception) { ASSERT_THROW({ allocator.allocate(1UL << 24UL); }, std::bad_alloc); - ASSERT_THROW({ allocator.allocate(allocator.max_size() + 1); }, - std::bad_array_new_length); + ASSERT_THROW( + { allocator.allocate(allocator.max_size() + 1); }, + std::bad_array_new_length); metall::logger::set_log_level(metall::logger::level::error); } TEST(StlAllocatorTest, Container) { { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); using element_type = std::pair; boost::interprocess::vector> vector( @@ -178,8 +183,7 @@ TEST(StlAllocatorTest, NestedContainer) { alloc_type>>>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); map_type map(manager.get_allocator<>()); for (uint64_t i = 0; i < 1024; ++i) { @@ -198,8 +202,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.construct("int")(10); ASSERT_EQ(*a, 10); @@ -211,7 +214,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); const auto ret1 = manager.find("int"); ASSERT_NE(ret1.first, nullptr); @@ -228,7 +231,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); ASSERT_FALSE(manager.destroy("int")); @@ -243,8 +246,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.find_or_construct("int")(10); ASSERT_EQ(*a, 10); @@ -255,7 +257,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); int *a = manager.find_or_construct("int")(20); ASSERT_EQ(*a, 10); @@ -267,7 +269,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); ASSERT_FALSE(manager.destroy("int")); @@ -289,8 +291,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { alloc_type>>>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); map_type *map = manager.construct("map")(manager.get_allocator<>()); (*map)[0].emplace_back(1); @@ -298,7 +299,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); map_type *map; std::size_t n; std::tie(map, n) = manager.find("map"); @@ -309,7 +310,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { } { - metall::manager manager(metall::open_read_only, dir_path().c_str()); + metall::manager manager(metall::open_read_only, dir_path()); map_type *map; std::size_t n; std::tie(map, n) = manager.find("map"); @@ -318,4 +319,5 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { ASSERT_EQ(map->at(0)[1], 2); ASSERT_EQ(map->at(1)[0], 3); } -} \ No newline at end of file +} +} // namespace \ No newline at end of file diff --git a/test/kernel/attributed_object_directory_test.cpp b/test/kernel/attributed_object_directory_test.cpp index c5b59750..3ee0cb87 100644 --- a/test/kernel/attributed_object_directory_test.cpp +++ b/test/kernel/attributed_object_directory_test.cpp @@ -176,7 +176,7 @@ TEST(AttributedObjectDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file(test_utility::make_test_path()); - ASSERT_TRUE(obj.serialize(file.c_str())); + ASSERT_TRUE(obj.serialize(file)); } TEST(AttributedObjectDirectoryTest, Deserialize) { @@ -187,12 +187,12 @@ TEST(AttributedObjectDirectoryTest, Deserialize) { directory_type obj; obj.insert("item1", 1, 2, 5); obj.insert("item2", 3, 4, 6, "description2"); - obj.serialize(file.c_str()); + obj.serialize(file); } { directory_type obj; - ASSERT_TRUE(obj.deserialize(file.c_str())); + ASSERT_TRUE(obj.deserialize(file)); // Get values correctly const auto itr1 = obj.find("item1"); diff --git a/test/kernel/bin_directory_test.cpp b/test/kernel/bin_directory_test.cpp index b1c38a9f..624e5d51 100644 --- a/test/kernel/bin_directory_test.cpp +++ b/test/kernel/bin_directory_test.cpp @@ -98,7 +98,7 @@ TEST(BinDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file = test_utility::make_test_path(); - ASSERT_TRUE(obj.serialize(file.c_str())); + ASSERT_TRUE(obj.serialize(file)); } TEST(BinDirectoryTest, Deserialize) { @@ -114,13 +114,13 @@ TEST(BinDirectoryTest, Deserialize) { obj.insert(num_small_bins - 1, 3); obj.insert(num_small_bins - 1, 4); - obj.serialize(file.c_str()); + obj.serialize(file); } { std::allocator allocator; directory_type obj(allocator); - ASSERT_TRUE(obj.deserialize(file.c_str())); + ASSERT_TRUE(obj.deserialize(file)); #ifdef METALL_USE_SORTED_BIN ASSERT_EQ(obj.front(0), 1); diff --git a/test/kernel/chunk_directory_test.cpp b/test/kernel/chunk_directory_test.cpp index 5e26bc39..8d8fdc64 100644 --- a/test/kernel/chunk_directory_test.cpp +++ b/test/kernel/chunk_directory_test.cpp @@ -124,7 +124,7 @@ TEST(ChunkDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file(test_utility::make_test_path()); - ASSERT_TRUE(directory.serialize(file.c_str())); + ASSERT_TRUE(directory.serialize(file)); } TEST(ChunkDirectoryTest, Deserialize) { @@ -146,13 +146,13 @@ TEST(ChunkDirectoryTest, Deserialize) { directory.insert(bin_no_mngr::num_small_bins()); // 1 chunk directory.insert(bin_no_mngr::num_small_bins() + 1); // 2 chunks - directory.serialize(file.c_str()); + directory.serialize(file); } { chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4); - ASSERT_TRUE(directory.deserialize(file.c_str())); + ASSERT_TRUE(directory.deserialize(file)); for (uint64_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { const auto bin_no = static_cast(i); const auto chunk_no = static_cast(i); diff --git a/test/kernel/manager_multithread_test.cpp b/test/kernel/manager_multithread_test.cpp index ac742faf..786472a6 100644 --- a/test/kernel/manager_multithread_test.cpp +++ b/test/kernel/manager_multithread_test.cpp @@ -99,7 +99,7 @@ template void run_alloc_dealloc_separated_test(const list_type &allocation_size_list) { // Allocate manager const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); // Main loop std::pair previous_allocation_rage(nullptr, nullptr); @@ -134,7 +134,7 @@ void run_alloc_dealloc_mixed_and_write_value_test( const list_type &allocation_size_list) { // Allocate manager const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); // Main loop std::vector> previous_addr_and_size_array( @@ -295,7 +295,7 @@ TEST(ManagerMultithreadsTest, ConstructAndFind) { constexpr std::size_t num_allocates = 1024; const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); std::vector keys; for (std::size_t i = 0; i < num_allocates; ++i) { diff --git a/test/kernel/manager_test.cpp b/test/kernel/manager_test.cpp index 491c8bb3..7c728338 100644 --- a/test/kernel/manager_test.cpp +++ b/test/kernel/manager_test.cpp @@ -5,6 +5,7 @@ #include "gtest/gtest.h" +#include #include #include @@ -12,6 +13,8 @@ #include "../test_utility.hpp" namespace { +namespace fs = std::filesystem; + using namespace metall::mtlldetail; using manager_type = metall::manager; @@ -23,23 +26,21 @@ using object_size_mngr = metall::kernel::object_size_manager; constexpr std::size_t k_min_object_size = object_size_mngr::at(0); -const std::string &dir_path() { - const static std::string path(test_utility::make_test_path()); +const fs::path &dir_path() { + const static fs::path path(test_utility::make_test_path()); return path; } TEST(ManagerTest, CreateAndOpenModes) { { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); ASSERT_TRUE(manager.destroy("int")); } { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto ret = manager.find("int"); ASSERT_EQ(ret.first, nullptr); ASSERT_FALSE(manager.destroy("int")); @@ -47,14 +48,13 @@ TEST(ManagerTest, CreateAndOpenModes) { } { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -63,20 +63,19 @@ TEST(ManagerTest, CreateAndOpenModes) { } { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -88,14 +87,13 @@ TEST(ManagerTest, CreateAndOpenModes) { TEST(ManagerTest, ConstructArray) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")[2](10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -105,7 +103,7 @@ TEST(ManagerTest, ConstructArray) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -114,20 +112,19 @@ TEST(ManagerTest, ConstructArray) { TEST(ManagerTest, findOrConstruct) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.find_or_construct("int")(10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int *a = manager.find_or_construct("int")(20); ASSERT_EQ(*a, 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -136,21 +133,20 @@ TEST(ManagerTest, findOrConstruct) { TEST(ManagerTest, findOrConstructArray) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.find_or_construct("int")[2](10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int *a = manager.find_or_construct("int")[2](20); ASSERT_EQ(a[0], 10); ASSERT_EQ(a[1], 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -160,16 +156,15 @@ TEST(ManagerTest, ConstructContainers) { { using vec_t = std::vector>; { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("vecs")[2]( 2, 10, manager.get_allocator()), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("vecs"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -183,7 +178,7 @@ TEST(ManagerTest, ConstructContainers) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("vecs")); ASSERT_TRUE(manager.all_memory_deallocated()); } @@ -193,15 +188,14 @@ TEST(ManagerTest, ConstructContainers) { TEST(ManagerTest, ConstructWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values[2] = {10, 20}; ASSERT_NE(manager.construct_it("int")[2](&values[0]), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -211,7 +205,7 @@ TEST(ManagerTest, ConstructWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -226,9 +220,8 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values1[2] = {10, 20}; float values2[2] = {0.1, 0.2}; ASSERT_NE(manager.construct_it("data")[2](&values1[0], &values2[0]), @@ -236,7 +229,7 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("data"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -248,7 +241,7 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("data")); } } @@ -257,16 +250,15 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { TEST(ManagerTest, FindOrConstructWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values[2] = {10, 20}; ASSERT_NE(manager.find_or_construct_it("int")[2](&values[0]), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int values[2] = {30, 40}; int *a = manager.find_or_construct_it("int")[2](&values[0]); ASSERT_NE(a, nullptr); @@ -275,7 +267,7 @@ TEST(ManagerTest, FindOrConstructWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -283,8 +275,8 @@ TEST(ManagerTest, FindOrConstructWithIterator) { TEST(ManagerTest, Destroy) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_FALSE(manager.destroy("named_obj")); ASSERT_FALSE(manager.destroy(metall::unique_instance)); @@ -307,8 +299,8 @@ TEST(ManagerTest, Destroy) { } { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj")(); manager.construct(metall::unique_instance)(); @@ -317,7 +309,7 @@ TEST(ManagerTest, Destroy) { // Destroy after restoring { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("named_obj")); ASSERT_TRUE(manager.destroy(metall::unique_instance)); @@ -329,8 +321,8 @@ TEST(ManagerTest, Destroy) { TEST(ManagerTest, DestroyPtr) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int *named_obj = manager.construct("named_obj")(); int *unique_obj = manager.construct(metall::unique_instance)(); @@ -351,8 +343,8 @@ TEST(ManagerTest, DestroyPtr) { } { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj")(); manager.construct(metall::unique_instance)(); @@ -364,7 +356,7 @@ TEST(ManagerTest, DestroyPtr) { // Destroy after restoring { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy_ptr(manager.find("named_obj").first)); ASSERT_TRUE( @@ -385,8 +377,8 @@ TEST(ManagerTest, DestroyDestruct) { // -- Check if destructors are called in destroy() -- // { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int count = 3; auto *data_obj = manager.construct("named_obj")(); @@ -406,8 +398,8 @@ TEST(ManagerTest, DestroyDestruct) { TEST(ManagerTest, GetInstanceName) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_STREQ( manager.get_instance_name(manager.construct("named_obj")()), @@ -424,7 +416,7 @@ TEST(ManagerTest, GetInstanceName) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_STREQ( manager.get_instance_name(manager.find("named_obj").first), "named_obj"); @@ -453,9 +445,9 @@ TEST(ManagerTest, ConstructException) { } }; - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); bool do_throw[2] = {false, true}; bool wrong_destroy = false; bool *flags[2] = {&wrong_destroy, &wrong_destroy}; @@ -477,9 +469,9 @@ TEST(ManagerTest, DestructException) { ~object() noexcept(false) { throw std::runtime_error(""); } }; - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct(metall::unique_instance)(); ASSERT_THROW(manager.destroy(metall::unique_instance), std::exception); @@ -488,8 +480,8 @@ TEST(ManagerTest, DestructException) { TEST(ManagerTest, GetInstanceType) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ(manager.get_instance_kind(manager.construct("named_obj")()), metall::manager::instance_kind::named_kind); @@ -505,7 +497,7 @@ TEST(ManagerTest, GetInstanceType) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_EQ(manager.get_instance_kind(manager.find("named_obj").first), metall::manager::instance_kind::named_kind); @@ -523,8 +515,8 @@ TEST(ManagerTest, GetInstanceType) { TEST(ManagerTest, GetInstanceLength) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ( manager.get_instance_length(manager.construct("named_obj")()), 1); @@ -553,7 +545,7 @@ TEST(ManagerTest, GetInstanceLength) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); { ASSERT_EQ( @@ -584,8 +576,8 @@ TEST(ManagerTest, GetInstanceLength) { TEST(ManagerTest, IsInstanceType) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_TRUE( manager.is_instance_type(manager.construct("named_obj")())); @@ -604,7 +596,7 @@ TEST(ManagerTest, IsInstanceType) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_TRUE( manager.is_instance_type(manager.find("named_obj").first)); @@ -624,8 +616,8 @@ TEST(ManagerTest, IsInstanceType) { TEST(ManagerTest, InstanceDescription) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto *named_obj = manager.construct("named_obj")(); std::string desc_name = "desc name"; @@ -651,7 +643,7 @@ TEST(ManagerTest, InstanceDescription) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); std::string buf; @@ -677,7 +669,7 @@ TEST(ManagerTest, InstanceDescription) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); std::string buf; @@ -706,8 +698,8 @@ TEST(ManagerTest, InstanceDescription) { TEST(ManagerTest, CountObjects) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ(manager.get_num_named_objects(), 0); manager.construct("named_obj1")(); @@ -746,8 +738,8 @@ TEST(ManagerTest, CountObjects) { ptrdiff_t anony_offset1 = 0; ptrdiff_t anony_offset2 = 0; { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj1")(); manager.construct(metall::unique_instance)(); @@ -762,7 +754,7 @@ TEST(ManagerTest, CountObjects) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_EQ(manager.get_num_named_objects(), 2); ASSERT_TRUE(manager.destroy("named_obj1")); @@ -788,8 +780,8 @@ TEST(ManagerTest, CountObjects) { TEST(ManagerTest, NamedObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -832,8 +824,8 @@ TEST(ManagerTest, NamedObjectIterator) { TEST(ManagerTest, UniqueObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -877,8 +869,8 @@ TEST(ManagerTest, UniqueObjectIterator) { TEST(ManagerTest, AnonymoustObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -924,9 +916,9 @@ TEST(ManagerTest, AnonymoustObjectIterator) { } TEST(ManagerTest, GetSegment) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto *obj = manager.construct(metall::unique_instance)(); ASSERT_EQ(manager.unique_begin()->offset() + static_cast(manager.get_address()), @@ -935,46 +927,46 @@ TEST(ManagerTest, GetSegment) { } TEST(ManagerTest, Consistency) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Must be inconsistent before closing - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); manager.construct("dummy")(10); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { // To make sure the consistent mark is cleared even after creating a new // data store using an old dir path - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); manager.construct("dummy")(10); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + manager_type manager(metall::open_only, dir_path()); + ASSERT_FALSE(manager_type::consistent(dir_path())); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); // Still consistent if it is opened with the read-only mode - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); } TEST(ManagerTest, TinyAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); const std::size_t alloc_size = k_min_object_size / 2; @@ -995,8 +987,8 @@ TEST(ManagerTest, TinyAllocation) { TEST(ManagerTest, SmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); const std::size_t alloc_size = k_min_object_size; @@ -1017,8 +1009,8 @@ TEST(ManagerTest, SmallAllocation) { TEST(ManagerTest, AllSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); for (std::size_t s = 1; s < k_chunk_size; ++s) manager.deallocate(manager.allocate(s)); } @@ -1026,8 +1018,8 @@ TEST(ManagerTest, AllSmallAllocation) { TEST(ManagerTest, MaxSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Max small allocation size const std::size_t alloc_size = @@ -1057,8 +1049,8 @@ TEST(ManagerTest, MaxSmallAllocation) { TEST(ManagerTest, MixedSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); const std::size_t alloc_size1 = k_min_object_size * 2; const std::size_t alloc_size2 = k_min_object_size * 4; @@ -1097,8 +1089,8 @@ TEST(ManagerTest, MixedSmallAllocation) { TEST(ManagerTest, LargeAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); // Assume that the object cache is not used for large allocation char *base_addr = nullptr; @@ -1132,8 +1124,8 @@ TEST(ManagerTest, LargeAllocation) { TEST(ManagerTest, AllMemoryDeallocated) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_TRUE(manager.all_memory_deallocated()); @@ -1153,8 +1145,8 @@ TEST(ManagerTest, AllMemoryDeallocated) { TEST(ManagerTest, AlignedAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); for (std::size_t alignment = k_min_object_size; alignment <= k_chunk_size; alignment *= 2) { @@ -1208,21 +1200,20 @@ TEST(ManagerTest, AlignedAllocation) { } TEST(ManagerTest, Flush) { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); manager.construct("int")(10); manager.flush(); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); } TEST(ManagerTest, AnonymousConstruct) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type *manager; - manager = - new manager_type(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager = new manager_type(metall::create_only, dir_path(), 1UL << 30UL); int *const a = manager->construct(metall::anonymous_instance)(); ASSERT_NE(a, nullptr); @@ -1238,10 +1229,9 @@ TEST(ManagerTest, AnonymousConstruct) { } TEST(ManagerTest, UniqueConstruct) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type *manager; - manager = - new manager_type(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager = new manager_type(metall::create_only, dir_path(), 1UL << 30UL); int *const a = manager->construct(metall::unique_instance)(); ASSERT_NE(a, nullptr); @@ -1263,44 +1253,44 @@ TEST(ManagerTest, UniqueConstruct) { } TEST(ManagerTest, UUID) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); std::string uuid; { - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type manager(metall::create_only, dir_path()); - uuid = manager_type::get_uuid(dir_path().c_str()); + uuid = manager_type::get_uuid(dir_path()); ASSERT_FALSE(uuid.empty()); } { // Returns the same UUID? - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_uuid(dir_path().c_str()), uuid); + manager_type manager(metall::open_only, dir_path()); + ASSERT_EQ(manager_type::get_uuid(dir_path()), uuid); } { // Returns a new UUID? - manager_type manager(metall::create_only, dir_path().c_str()); - ASSERT_NE(manager_type::get_uuid(dir_path().c_str()), uuid); + manager_type manager(metall::create_only, dir_path()); + ASSERT_NE(manager_type::get_uuid(dir_path()), uuid); } } TEST(ManagerTest, Version) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_version(dir_path().c_str()), METALL_VERSION); + manager_type manager(metall::create_only, dir_path()); + ASSERT_EQ(manager_type::get_version(dir_path()), METALL_VERSION); } { - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_version(dir_path().c_str()), METALL_VERSION); + manager_type manager(metall::open_only, dir_path()); + ASSERT_EQ(manager_type::get_version(dir_path()), METALL_VERSION); } } TEST(ManagerTest, Description) { // Set and get with non-static method { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); ASSERT_TRUE(manager.set_description("description1")); std::string description; @@ -1311,23 +1301,21 @@ TEST(ManagerTest, Description) { // Get with static method { std::string description; - ASSERT_TRUE( - manager_type::get_description(dir_path().c_str(), &description)); + ASSERT_TRUE(manager_type::get_description(dir_path(), &description)); ASSERT_STREQ(description.c_str(), "description1"); } // Set with static method { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type manager(metall::create_only, - dir_path().c_str()); // Make a new data store - ASSERT_TRUE( - manager_type::set_description(dir_path().c_str(), "description2")); + dir_path()); // Make a new data store + ASSERT_TRUE(manager_type::set_description(dir_path(), "description2")); } // Get with non-static method { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); std::string description; ASSERT_TRUE(manager.get_description(&description)); ASSERT_STREQ(description.c_str(), "description2"); @@ -1341,13 +1329,13 @@ TEST(ManagerTest, CheckSanity) { metall::logger::abort_on_critical_error(false); { - auto *manager = new manager_type(metall::create_only, dir_path().c_str()); + auto *manager = new manager_type(metall::create_only, dir_path()); ASSERT_TRUE(manager->check_sanity()); } { auto *bad_manager = - new manager_type(metall::open_only, (dir_path() + "-invalid").c_str()); + new manager_type(metall::open_only, dir_path().string() + "-invalid"); ASSERT_FALSE(bad_manager->check_sanity()); } } diff --git a/test/kernel/multimanager_test.cpp b/test/kernel/multimanager_test.cpp index 26b4140b..23211406 100644 --- a/test/kernel/multimanager_test.cpp +++ b/test/kernel/multimanager_test.cpp @@ -34,9 +34,9 @@ TEST(MultiManagerTest, SingleThread) { const auto dir_path2(test_utility::make_test_path(std::to_string(2))); { - manager_type manager1(metall::create_only, dir_path1.c_str(), + manager_type manager1(metall::create_only, dir_path1, k_chunk_size * 8); - manager_type manager2(metall::create_only, dir_path2.c_str(), + manager_type manager2(metall::create_only, dir_path2, k_chunk_size * 8); vector_type *vector1 = @@ -52,8 +52,8 @@ TEST(MultiManagerTest, SingleThread) { } { - manager_type manager1(metall::open_only, dir_path1.c_str()); - manager_type manager2(metall::open_only, dir_path2.c_str()); + manager_type manager1(metall::open_only, dir_path1); + manager_type manager2(metall::open_only, dir_path2); vector_type *vector1; std::size_t n1; @@ -73,8 +73,8 @@ TEST(MultiManagerTest, SingleThread) { } { - manager_type manager1(metall::open_only, dir_path1.c_str()); - manager_type manager2(metall::open_only, dir_path2.c_str()); + manager_type manager1(metall::open_only, dir_path1); + manager_type manager2(metall::open_only, dir_path2); vector_type *vector1; std::size_t n1; @@ -111,7 +111,7 @@ TEST(MultiManagerTest, MultiThread) { const auto dir_path(test_utility::make_test_path( "/" + std::to_string(omp::get_thread_num()))); - manager_type manager(metall::create_only, dir_path.c_str(), + manager_type manager(metall::create_only, dir_path, k_chunk_size * 16); vector_type *vector = manager.construct("vector")(manager.get_allocator<>()); @@ -123,7 +123,7 @@ TEST(MultiManagerTest, MultiThread) { for (int t = 0; t < get_num_threads(); ++t) { const auto dir_path(test_utility::make_test_path("/" + std::to_string(t))); - manager_type manager(metall::open_only, dir_path.c_str()); + manager_type manager(metall::open_only, dir_path); vector_type *vector; std::size_t n; diff --git a/test/kernel/object_attribute_accessor_test.cpp b/test/kernel/object_attribute_accessor_test.cpp index e1f9b96f..335be02e 100644 --- a/test/kernel/object_attribute_accessor_test.cpp +++ b/test/kernel/object_attribute_accessor_test.cpp @@ -15,24 +15,24 @@ using namespace metall; auto attr_accessor_named() { return manager::access_named_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } auto attr_accessor_unique() { return manager::access_unique_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } auto attr_accessor_anonymous() { return manager::access_anonymous_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } TEST(ObjectAttributeAccessorTest, Constructor) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } @@ -42,10 +42,10 @@ TEST(ObjectAttributeAccessorTest, Constructor) { } TEST(ObjectAttributeAccessorTest, NumObjects) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } @@ -56,7 +56,7 @@ TEST(ObjectAttributeAccessorTest, NumObjects) { } { - manager mngr(open_only, test_utility::make_test_path().c_str()); + manager mngr(open_only, test_utility::make_test_path()); mngr.construct("int1")(); mngr.construct("int2")(); mngr.construct(unique_instance)(); @@ -71,10 +71,10 @@ TEST(ObjectAttributeAccessorTest, NumObjects) { } TEST(ObjectAttributeAccessorTest, Count) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } diff --git a/test/kernel/snapshot_test.cpp b/test/kernel/snapshot_test.cpp index 3f4b25cf..f0ad8175 100644 --- a/test/kernel/snapshot_test.cpp +++ b/test/kernel/snapshot_test.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" #include +#include #include @@ -13,42 +14,44 @@ namespace { -std::string original_dir_path() { - const std::string path(test_utility::make_test_path("original")); +namespace fs = std::filesystem; + +fs::path original_dir_path() { + const fs::path path(test_utility::make_test_path("original")); return path; } -std::string snapshot_dir_path() { - const std::string path(test_utility::make_test_path("snapshot")); +fs::path snapshot_dir_path() { + const fs::path path(test_utility::make_test_path("snapshot")); return path; } TEST(SnapshotTest, Snapshot) { - metall::manager::remove(original_dir_path().c_str()); - metall::manager::remove(snapshot_dir_path().c_str()); + metall::manager::remove(original_dir_path()); + metall::manager::remove(snapshot_dir_path()); { - metall::manager manager(metall::create_only, original_dir_path().c_str()); + metall::manager manager(metall::create_only, original_dir_path()); [[maybe_unused]] auto a = manager.construct("a")(1); [[maybe_unused]] auto b = manager.construct(metall::unique_instance)(2); - ASSERT_TRUE(manager.snapshot(snapshot_dir_path().c_str())); - ASSERT_TRUE(metall::manager::consistent(snapshot_dir_path().c_str())); + ASSERT_TRUE(manager.snapshot(snapshot_dir_path())); + ASSERT_TRUE(metall::manager::consistent(snapshot_dir_path())); // UUID const auto original_uuid = - metall::manager::get_uuid(original_dir_path().c_str()); + metall::manager::get_uuid(original_dir_path()); ASSERT_FALSE(original_uuid.empty()); const auto snapshot_uuid = - metall::manager::get_uuid(snapshot_dir_path().c_str()); + metall::manager::get_uuid(snapshot_dir_path()); ASSERT_FALSE(snapshot_uuid.empty()); ASSERT_NE(original_uuid, snapshot_uuid); // Version - ASSERT_EQ(metall::manager::get_version(original_dir_path().c_str()), - metall::manager::get_version(snapshot_dir_path().c_str())); + ASSERT_EQ(metall::manager::get_version(original_dir_path()), + metall::manager::get_version(snapshot_dir_path())); } { diff --git a/test/test_utility.hpp b/test/test_utility.hpp index fd656bd8..f8bae3b4 100644 --- a/test/test_utility.hpp +++ b/test/test_utility.hpp @@ -10,20 +10,26 @@ #include #include +#include_next +#include #include namespace test_utility { +namespace { +namespace fs = std::filesystem; +} + const char *k_test_dir_env_name = "METALL_TEST_DIR"; const char *k_default_test_dir = "/tmp/metall_test_dir"; namespace detail { -inline std::string get_test_dir() { +inline fs::path get_test_dir() { if (const char *env_p = std::getenv(k_test_dir_env_name)) { - return std::string(env_p); + return fs::path(env_p); } - return std::string(k_default_test_dir); + return fs::path(k_default_test_dir); } } // namespace detail @@ -33,11 +39,14 @@ inline bool create_test_dir() { return true; } -inline std::string make_test_path(const std::string &name = std::string()) { - return detail::get_test_dir() + "/metalltest" + "-" + - ::testing::UnitTest::GetInstance()->current_test_case()->name() + "-" + - ::testing::UnitTest::GetInstance()->current_test_info()->name() + "-" + - name; +inline fs::path make_test_path(const fs::path &name = fs::path()) { + std::stringstream file_name; + file_name << "metalltest-" + << ::testing::UnitTest::GetInstance()->current_test_case()->name() + << "-" + << ::testing::UnitTest::GetInstance()->current_test_info()->name() + << "-" << name.string(); + return detail::get_test_dir() / file_name.str(); } } // namespace test_utility From bce467cac96605a8ebd052336fc1b7d21796b9bb Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Mon, 18 Dec 2023 19:09:08 -0800 Subject: [PATCH 03/26] Bugfix --- include/metall/detail/file.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index c528ee83..1cec50ad 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -310,8 +310,8 @@ inline bool copy_file_dense(const fs::path &source_path, #ifdef __linux__ inline bool copy_file_sparse_linux(const fs::path &source_path, const fs::path &destination_path) { - std::string command("cp --sparse=auto " + source_path + " " + - destination_path); + std::string command("cp --sparse=auto " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); const bool success = (status != -1) && !!(WIFEXITED(status)); if (!success) { From 7b530232f7d2b180ef92bdd1412a80dc736956f8 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Tue, 19 Dec 2023 07:55:55 -0800 Subject: [PATCH 04/26] Use std::filesystem::path --- include/metall/detail/file.hpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index 1cec50ad..fa63259d 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -256,9 +256,8 @@ inline ssize_t get_actual_file_size(const fs::path &file_path) { /// \return Upon successful completion, returns true; otherwise, false is /// returned. If the file or directory does not exist, true is returned. inline bool remove_file(const fs::path &path) { - std::filesystem::path p(path); std::error_code ec; - [[maybe_unused]] const auto num_removed = std::filesystem::remove_all(p, ec); + std::filesystem::remove_all(path, ec); return !ec; } @@ -331,8 +330,8 @@ inline bool copy_file_sparse_linux(const fs::path &source_path, /// \param destination_path A destination path. /// \param sparse_copy If true is specified, tries to perform sparse file copy. /// \return On success, returns true. On error, returns false. -inline bool copy_file(const std::string &source_path, - const std::string &destination_path, +inline bool copy_file(const fs::path &source_path, + const fs::path &destination_path, const bool sparse_copy = true) { if (sparse_copy) { #ifdef __linux__ @@ -353,8 +352,8 @@ inline bool copy_file(const std::string &source_path, /// \param file_list A buffer to put results. /// \return Returns true if there is no error (empty directory returns true as /// long as the operation does not fail). Returns false on error. -inline bool get_regular_file_names(const std::string &dir_path, - std::vector *file_list) { +inline bool get_regular_file_names(const fs::path &dir_path, + std::vector *file_list) { if (!directory_exist(dir_path)) { return false; } @@ -385,11 +384,10 @@ inline bool get_regular_file_names(const std::string &dir_path, /// \param copy_func The actual copy function. /// \return On success, returns true. On error, returns false. inline bool copy_files_in_directory_in_parallel_helper( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads, - const std::function - ©_func) { - std::vector src_file_names; + const std::function ©_func) { + std::vector src_file_names; if (!get_regular_file_names(source_dir_path, &src_file_names)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to get file list"); @@ -403,10 +401,9 @@ inline bool copy_files_in_directory_in_parallel_helper( while (true) { const auto file_no = file_no_cnt.fetch_add(1); if (file_no >= src_file_names.size()) break; - const std::string &src_file_path = - source_dir_path + "/" + src_file_names[file_no]; - const std::string &dst_file_path = - destination_dir_path + "/" + src_file_names[file_no]; + const fs::path &src_file_path = source_dir_path / src_file_names[file_no]; + const fs::path &dst_file_path = + destination_dir_path / src_file_names[file_no]; num_successes.fetch_add(copy_func(src_file_path, dst_file_path) ? 1 : 0); } }; @@ -436,11 +433,11 @@ inline bool copy_files_in_directory_in_parallel_helper( /// \param sparse_copy Performs sparse file copy. /// \return On success, returns true. On error, returns false. inline bool copy_files_in_directory_in_parallel( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads, const bool sparse_copy = true) { return copy_files_in_directory_in_parallel_helper( source_dir_path, destination_dir_path, max_num_threads, - [&sparse_copy](const std::string &src, const std::string &dst) -> bool { + [&sparse_copy](const fs::path &src, const fs::path &dst) -> bool { return copy_file(src, dst, sparse_copy); }); } From acf81c851097a8c6ae20bb0a3d0e0c66514f0bd4 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Fri, 22 Dec 2023 14:35:53 -0800 Subject: [PATCH 05/26] backup --- include/metall/detail/file.hpp | 89 ++++++++++++++++--- verification/CMakeLists.txt | 3 +- verification/sparse_copy/CMakeLists.txt | 1 + .../sparse_copy/verify_sparse_copy.cpp | 87 ++++++++++++++++++ 4 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 verification/sparse_copy/CMakeLists.txt create mode 100644 verification/sparse_copy/verify_sparse_copy.cpp diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index fa63259d..88014901 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -89,7 +89,7 @@ inline bool extend_file_size_manually(const int fd, const off_t offset, for (off_t i = offset; i < file_size / 4096 + offset; ++i) { ::pwrite(fd, buffer, 4096, i * 4096); } - const size_t remained_size = file_size % 4096; + const std::size_t remained_size = file_size % 4096; if (remained_size > 0) ::pwrite(fd, buffer, remained_size, file_size - remained_size); @@ -100,7 +100,7 @@ inline bool extend_file_size_manually(const int fd, const off_t offset, return ret; } -inline bool extend_file_size(const int fd, const size_t file_size, +inline bool extend_file_size(const int fd, const std::size_t file_size, const bool fill_with_zero) { if (fill_with_zero) { #ifdef __APPLE__ @@ -116,8 +116,7 @@ inline bool extend_file_size(const int fd, const size_t file_size, } #endif } else { - // ----- extend the file if its size is smaller than that of mapped area - // ----- // + // extend the file if its size is smaller than that of mapped area struct stat stat_buf; if (::fstat(fd, &stat_buf) == -1) { logger::perror(logger::level::error, __FILE__, __LINE__, "fstat"); @@ -135,7 +134,8 @@ inline bool extend_file_size(const int fd, const size_t file_size, return ret; } -inline bool extend_file_size(const fs::path &file_path, const size_t file_size, +inline bool extend_file_size(const fs::path &file_path, + const std::size_t file_size, const bool fill_with_zero = false) { const int fd = ::open(file_path.c_str(), O_RDWR); if (fd == -1) { @@ -249,7 +249,7 @@ inline ssize_t get_actual_file_size(const fs::path &file_path) { logger::perror(logger::level::error, __FILE__, __LINE__, s.c_str()); return -1; } - return stat_buf.st_blocks * 512LL; + return ssize_t(stat_buf.st_blocks) * ssize_t(stat_buf.st_blksize); } /// \brief Remove a file or directory @@ -280,7 +280,7 @@ inline bool free_file_space([[maybe_unused]] const int fd, #endif } -namespace file_copy_detail { +namespace fcpdtl { inline bool copy_file_dense(const fs::path &source_path, const fs::path &destination_path) { @@ -306,6 +306,74 @@ inline bool copy_file_dense(const fs::path &source_path, return success; } +inline bool copy_file_sparse_manually(const fs::path &source_path, + const fs::path &destination_path) { + const auto src_size = get_file_size(source_path); + if (src_size == -1) { + return false; + } + if (!extend_file_size(destination_path, src_size, false)) { + return false; + } + + std::ifstream source; + std::ofstream dest; + const auto open_file = [](const auto &path, auto &file) { + file.open(path, std::ios::binary); + if (!file.is_open()) { + std::stringstream ss; + ss << "Failed to open file: " << path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return false; + } + return true; + }; + + if (!open_file(source_path, source) || !open_file(destination_path, dest)) { + return false; + } + + const std::size_t block_size = 512; + char buffer[block_size]; + + const auto is_sparse = [](const char *const buffer, const std::size_t size) { + constexpr std::size_t chunk_size = sizeof(uint64_t); + const std::size_t num_chunks = size / chunk_size; + for (std::size_t i = 0; i < num_chunks; ++i) { + if (*reinterpret_cast(buffer + i * chunk_size) != + uint64_t(0)) { + return false; + } + } + // Check the remaining bytes + for (std::size_t i = num_chunks * chunk_size; i < size; ++i) { + if (buffer[i] != char(0)) { + return false; + } + } + return true; + }; + + while (source.read(buffer, block_size) || source.gcount() > 0) { + if (!is_sparse(buffer, source.gcount())) { + dest.write(buffer, source.gcount()); + } else { + dest.seekp(source.gcount(), std::ios_base::cur); + } + } + + source.close(); + dest.close(); + if (!dest) { + std::stringstream ss; + ss << "Failed to write file: " << destination_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return false; + } + + return true; +} + #ifdef __linux__ inline bool copy_file_sparse_linux(const fs::path &source_path, const fs::path &destination_path) { @@ -323,7 +391,7 @@ inline bool copy_file_sparse_linux(const fs::path &source_path, } #endif -} // namespace file_copy_detail +} // namespace fcpdtl /// \brief Copy a file. /// \param source_path A source file path. @@ -335,14 +403,13 @@ inline bool copy_file(const fs::path &source_path, const bool sparse_copy = true) { if (sparse_copy) { #ifdef __linux__ - return file_copy_detail::copy_file_sparse_linux(source_path, - destination_path); + return fcpdtl::copy_file_sparse_linux(source_path, destination_path); #else logger::out(logger::level::info, __FILE__, __LINE__, "Sparse file copy is not available"); #endif } - return file_copy_detail::copy_file_dense(source_path, destination_path); + return fcpdtl::copy_file_dense(source_path, destination_path); } /// \brief Get the file names in a directory. diff --git a/verification/CMakeLists.txt b/verification/CMakeLists.txt index 66d65804..819839da 100644 --- a/verification/CMakeLists.txt +++ b/verification/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(mmap) add_subdirectory(object_size) add_subdirectory(logger) add_subdirectory(soft_dirty) -add_subdirectory(file_io) \ No newline at end of file +add_subdirectory(file_io) +add_subdirectory(sparse_copy) \ No newline at end of file diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt new file mode 100644 index 00000000..ac13e8f9 --- /dev/null +++ b/verification/sparse_copy/CMakeLists.txt @@ -0,0 +1 @@ +add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy.cpp b/verification/sparse_copy/verify_sparse_copy.cpp new file mode 100644 index 00000000..d69e45f5 --- /dev/null +++ b/verification/sparse_copy/verify_sparse_copy.cpp @@ -0,0 +1,87 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include +#include +#include + +#include +#include + +namespace mdtl = metall::mtlldetail; + +int main() { + const std::filesystem::path src_path = "source.dat"; + const ssize_t size = 1024 * 1024 * 32; + + if (!mdtl::create_file(src_path)) { + std::cerr << "Failed to create a file" << std::endl; + return EXIT_FAILURE; + } + + if (!mdtl::extend_file_size(src_path, size)) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + { + auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); + if (!map) { + std::cerr << "Failed to map a file" << std::endl; + return EXIT_FAILURE; + } + auto* buf = reinterpret_cast(map); + buf[0] = 1; + buf[1024 * 1024 - 1] = 1; + if (!mdtl::munmap(fd, map, size, true)) { + std::cerr << "Failed to unmap a file" << std::endl; + return EXIT_FAILURE; + } + } + + if (mdtl::get_file_size(src_path) < size) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + if (mdtl::get_actual_file_size(src_path) >= size) { + std::cerr << "Failed to create a sparse file" << std::endl; + std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) + << std::endl; + return EXIT_FAILURE; + } + + // Sparse copy + const std::filesystem::path dst_path = "destination.dat"; + if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { + std::cerr << "Failed to copy a file" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Source file size: " << mdtl::get_file_size(src_path) + << std::endl; + std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) + << std::endl; + + // Show actual size + std::cout << "Source actual file size: " + << mdtl::get_actual_file_size(src_path) << std::endl; + std::cout << "Destination actual file size: " + << mdtl::get_actual_file_size(dst_path) << std::endl; + + // if (mdtl::get_file_size(dst_path) < size) { + // std::cerr << "Failed to extend file size" << std::endl; + // return EXIT_FAILURE; + // } + +// if (mdtl::get_actual_file_size(dst_path) >= size) { +// std::cerr << "Failed to create a sparse file" << std::endl; +// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) +// << std::endl; +// return EXIT_FAILURE; +// } + + return 0; +} \ No newline at end of file From 30c1302eb67b69faa46f45252164e9f72f26ff66 Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Wed, 22 Nov 2023 19:28:53 -0800 Subject: [PATCH 06/26] Bugfix in documentations --- README.md | 42 +++++++++++++++++++++++++----------------- mkdocs.yml | 4 +++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 68d32525..56250ede 100644 --- a/README.md +++ b/README.md @@ -5,22 +5,23 @@ Metall: A Persistent Memory Allocator for Data-Centric Analytics =============================================== -* Provides rich memory allocation interfaces for C++ applications that - use persistent memory devices to persistently store heap data on such - devices. -* Creates files in persistent memory and maps them into virtual memory - space so that users can access the mapped region just as normal memory - regions allocated in DRAM. -* Actual persistent memory hardware could be any non-volatile memory (NVM) with file system support. -* To provide persistent memory allocation, Metall employs concepts and - APIs developed by - [Boost.Interprocess](https://www.boost.org/doc/libs/1_69_0/doc/html/interprocess.html). -* Supports multi-thread -* Also provides a space-efficient snapshot/versioning, leveraging reflink - copy mechanism in filesystem. In case reflink is not supported, Metall - automatically falls back to regular copy. -* See details: [Metall overview slides](docs/publications/metall_101.pdf). - +- A memory allocator enables applications to transparently allocate data into a file system. + + - From applications, Metall looks like a normal heap allocator. + + - Applications can keep their data beyond single process life cycles and reattach the data in succeeding runs. + + - Leverages the memory-mapped file mechanism (i.e., [mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html)) to _map_ application data in files to the main memory. + +- Employs the API developed by [Boost.Interprocess](https://www.boost.org/doc/libs/1_69_0/doc/html/interprocess.html). + + - Useful for allocating C++ data structures (including STL containers). + +- Incorporates state-of-the-art allocation algorithms. + +- Provides a space-efficient snapshot/versioning, leveraging the reflink copy mechanism in file systems. + +- See details: [Metall overview slides](docs/publications/metall_101.pdf). # Getting Started @@ -62,7 +63,9 @@ However, we haven't tested it intensively. Also, Boost C++ Libraries 1.69 or more may be required if one wants to build Metall with Clang + CUDA. -## Metall with Spack +## Package Manager Support + +### Metall with Spack Metall package is also available on [Spack](https://spack.io/). @@ -82,6 +85,11 @@ spack load metall g++ -std=c++17 your_program.cpp -lstdc++fs -I${BOOST_ROOT}/include -I${METALL_ROOT}/include ``` +### Metall with Connan + +Metall is also available on [Conan](https://conan.io/), thanks to the [DICE](https://github.com/dice-group) research group. +Conan Metall package information is [here](https://conan.io/center/recipes/metall). + ## Use Metall from Another CMake Project diff --git a/mkdocs.yml b/mkdocs.yml index 632147ad..4051bc63 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,9 @@ +# Required +version: 2 site_name: Metall site_author: 'Keita Iwabuchi (LLNL), Roger A Pearce (LLNL), and Maya B Gokhale (LLNL)' -copyright: Copyright 2020 Lawrence Livermore National Security, LLC +copyright: Copyright 2023 Lawrence Livermore National Security, LLC docs_dir: 'docs/readthedocs' theme: 'readthedocs' From 3ea1c2269720bda612a39cafa07758bcab75738d Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Wed, 22 Nov 2023 19:45:45 -0800 Subject: [PATCH 07/26] Bugfix in read the docs --- .readthedocs.yaml | 4 ++++ mkdocs.yml | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..77719fcf --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,4 @@ +version: 2 + +mkdocs: + configuration: mkdocs.yml diff --git a/mkdocs.yml b/mkdocs.yml index 4051bc63..52811c0d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,3 @@ -# Required -version: 2 - site_name: Metall site_author: 'Keita Iwabuchi (LLNL), Roger A Pearce (LLNL), and Maya B Gokhale (LLNL)' copyright: Copyright 2023 Lawrence Livermore National Security, LLC From 95e57c841ad26660e9b14a8377bf949fde989563 Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Wed, 22 Nov 2023 19:58:04 -0800 Subject: [PATCH 08/26] Bugfix in read the docs --- .readthedocs.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 77719fcf..520ddfe9 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,4 +1,9 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.12" + mkdocs: configuration: mkdocs.yml From 06cbc2bbfc943965acce5a8dee634a253294f7f4 Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Mon, 27 Nov 2023 18:38:30 -0800 Subject: [PATCH 09/26] Bugfix in freeing object cache data --- include/metall/kernel/object_cache.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/metall/kernel/object_cache.hpp b/include/metall/kernel/object_cache.hpp index 23ac3aa8..6bf08848 100644 --- a/include/metall/kernel/object_cache.hpp +++ b/include/metall/kernel/object_cache.hpp @@ -544,6 +544,10 @@ class object_cache { } private: + struct free_deleter { + void operator()(void *const p) const noexcept { std::free(p); } + }; + inline static unsigned int priv_get_num_cpus() { return mdtl::get_num_cpus(); } @@ -755,7 +759,7 @@ class object_cache { #ifdef METALL_ENABLE_MUTEX_IN_OBJECT_CACHE std::vector m_mutex; #endif - std::unique_ptr m_cache{nullptr}; + std::unique_ptr m_cache{nullptr}; }; // const_bin_iterator From 96e322e308cc610ce44b5315703309171d816960 Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Mon, 27 Nov 2023 19:43:46 -0800 Subject: [PATCH 10/26] Brush up on comments --- include/metall/kernel/object_cache.hpp | 120 +++++++++++++------------ 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/include/metall/kernel/object_cache.hpp b/include/metall/kernel/object_cache.hpp index 6bf08848..e47eab5e 100644 --- a/include/metall/kernel/object_cache.hpp +++ b/include/metall/kernel/object_cache.hpp @@ -38,19 +38,17 @@ namespace mdtl = metall::mtlldetail; namespace obcdetail { -/// A cache block is a unit of memory that contains cached objects -/// (specifically, a cached object is difference_type). -/// Cache blocks are members of two linked-lists. -/// One is a linked-list of all cache blocks in the cache. -/// The other is a linked-list of cache blocks in the same bin. -/// The linked-lists are used to manage the order of cache blocks. -/// The order of cache blocks is used to determine which cache block is -/// evicted when the cache is full. +/// A cache block contains offsets of cached objects of the same bin (object +/// size). The maximum number of objects in a cache block is 'k_capacity'. Cache +/// blocks compose two double-linked lists: 1) a linked list of cache blocks of +/// the same bin. 2) a linked list of cache blocks of any bin. template struct cache_block { static constexpr unsigned int k_capacity = 64; + // Disable them to avoid unexpected calls. cache_block() = delete; + ~cache_block() = delete; inline void clear() { bin_no = std::numeric_limits::max(); @@ -96,10 +94,10 @@ struct cache_block { difference_type cache[k_capacity]; }; -/// A bin header is a unit of memory that contains information about a bin -/// within a cache. Specifically, it contains the active block and the number of -/// objects in the active block. The active block is the block that is currently -/// used to cache objects. Non-active blocks are always full. +/// A bin header contains a pointer to the active block of the corresponding bin +/// and the number of objects in the active block. +/// Inserting and removing objects are done only to the active block. Non-active +/// blocks are always full. template class bin_header { public: @@ -112,7 +110,7 @@ class bin_header { m_active_block = nullptr; } - // Move the active block to the next block + // Move the active block to the next (older) block inline void move_to_next_active_block() { if (!m_active_block) return; m_active_block = m_active_block->bin_older_block; @@ -147,14 +145,12 @@ class bin_header { const cache_block_type *m_active_block{nullptr}; }; -/// A free blocks list is a linked-list of free blocks. -/// It is used to manage free blocks. -/// Cache blocks are located in a contiguous memory region. -/// All cache blocks are uninitialized at the beginning --- -/// thus, they do not consume physical memory. -/// This free list is designed such that it does not touch uninitialized blocks -/// until they are used. This design is crucial to reduce Metall manager -/// construction time. +/// A free blocks list contains a linked-list of free blocks. +/// This class assumes that A) bocks are located in a contiguous memory region +/// and B) all cache blocks are uninitialized at the beginning so that they do +/// not consume physical memory. This free list is designed such that it does +/// not touch uninitialized blocks until they are used. This design is crucial +/// to reduce Metall manager construction time. template class free_blocks_list { public: @@ -207,14 +203,13 @@ class free_blocks_list { // Blocks that were used and became empty const cache_block_type *m_blocks; // The top block of the uninitialized blocks. - // Uninitialized blocks are located in a contiguous memory region. const cache_block_type *m_uninit_top; const cache_block_type *m_last_block; }; -/// A cache header is a unit of memory that contains information about a cache. -/// Specifically, it contains the total size of objects in the cache, -/// the oldest and newest active blocks, and a free blocks list. +/// A cache header contains some metadata of a single cache. +/// Specifically, it contains the total size (byte) of objects in the cache, +/// the pointers to the oldest and the newest blocks, and a free blocks list. template struct cache_header { private: @@ -231,24 +226,24 @@ struct cache_header { void clear() { m_total_size_byte = 0; - m_oldest_active_block = nullptr; - m_newest_active_block = nullptr; + m_oldest_block = nullptr; + m_newest_block = nullptr; m_free_blocks.clear(); } inline void unregister(const cache_block_type *const block) { - if (block == m_newest_active_block) { - m_newest_active_block = block->older_block; + if (block == m_newest_block) { + m_newest_block = block->older_block; } - if (block == m_oldest_active_block) { - m_oldest_active_block = block->newer_block; + if (block == m_oldest_block) { + m_oldest_block = block->newer_block; } } inline void register_new_block(const cache_block_type *const block) { - m_newest_active_block = block; - if (!m_oldest_active_block) { - m_oldest_active_block = block; + m_newest_block = block; + if (!m_oldest_block) { + m_oldest_block = block; } } @@ -258,20 +253,20 @@ struct cache_header { return m_total_size_byte; } - inline cache_block_type *newest_active_block() noexcept { - return const_cast(m_newest_active_block); + inline cache_block_type *newest_block() noexcept { + return const_cast(m_newest_block); } - inline const cache_block_type *newest_active_block() const noexcept { - return m_newest_active_block; + inline const cache_block_type *newest_block() const noexcept { + return m_newest_block; } - inline cache_block_type *oldest_active_block() noexcept { - return const_cast(m_oldest_active_block); + inline cache_block_type *oldest_block() noexcept { + return const_cast(m_oldest_block); } - inline const cache_block_type *oldest_active_block() const noexcept { - return m_oldest_active_block; + inline const cache_block_type *oldest_block() const noexcept { + return m_oldest_block; } inline free_blocks_list_type &free_blocks() noexcept { return m_free_blocks; } @@ -282,13 +277,14 @@ struct cache_header { private: std::size_t m_total_size_byte; - const cache_block_type *m_oldest_active_block{nullptr}; - const cache_block_type *m_newest_active_block{nullptr}; + const cache_block_type *m_oldest_block{nullptr}; + const cache_block_type *m_newest_block{nullptr}; free_blocks_list_type m_free_blocks; }; -/// A cache container is a unit of memory that contains all data structures that -/// constitute a cache. +/// A cache container contains all data that constitute a cache. +/// This cache container holds a cache header, bin headers, and cache blocks in +/// a contiguous memory region. template struct cache_container { @@ -296,6 +292,13 @@ struct cache_container { using bin_header_type = bin_header; using cacbe_block_type = cache_block; + // Disable the default constructor to avoid unexpected initialization. + cache_container() = delete; + + // Disable the copy constructor to avoid unexpected destructor call. + ~cache_container() = delete; + + // This data structure must be initialized first using this function. void init() { new (&header) cache_heaer_type(blocks, num_blocks_per_cache); // Memo: The in-place an array construction may not be supported by some @@ -381,7 +384,12 @@ inline constexpr std::size_t comp_num_blocks_per_cache( } // namespace obcdetail -/// \brief A cache for small objects. +/// A cache for small objects. +/// This class manages per-'CPU' (i.e., CPU-core rather than 'socket') caches +/// internally. Actually stored data is the offsets of cached objects. This +/// cache push and pop objects using a LIFO policy. When the cache is full +/// (exceeds a pre-defined threshold), it deallocates some oldest objects first +/// before caching new ones. template class object_cache { @@ -469,7 +477,7 @@ class object_cache { object_cache &operator=(const object_cache &) = default; object_cache &operator=(object_cache &&) noexcept = default; - /// \brief Pop an object offset from the cache. + /// Pop an object offset from the cache. /// If the cache is empty, allocate objects and cache them first. difference_type pop(const bin_no_type bin_no, object_allocator_type *const allocator_instance, @@ -479,8 +487,8 @@ class object_cache { deallocator_function); } - /// \brief Cache an object. - /// If the cache is full, deallocate some cached objects first. + /// Cache an object. + /// If the cache is full, deallocate some oldest cached objects first. /// Return false if an error occurs. bool push(const bin_no_type bin_no, const difference_type object_offset, object_allocator_type *const allocator_instance, @@ -489,7 +497,7 @@ class object_cache { deallocator_function); } - /// \brief Clear all cached objects. + /// Clear all cached objects. /// Cached objects are going to be deallocated. void clear(object_allocator_type *const allocator_instance, object_deallocate_func_type deallocator_function) { @@ -569,7 +577,7 @@ class object_cache { #endif } - /// \brief Get CPU number. + /// Get CPU number. /// This function does not call the system call every time as it is slow. inline static size_type priv_get_cpu_no() { #ifdef METALL_DISABLE_CONCURRENCY @@ -650,7 +658,7 @@ class object_cache { new_block->cache); // Link the new block to the existing blocks - new_block->link_to_older(cache_header.newest_active_block(), + new_block->link_to_older(cache_header.newest_block(), bin_header.active_block()); // Update headers @@ -701,7 +709,7 @@ class object_cache { assert(free_block); free_block->clear(); free_block->bin_no = bin_no; - free_block->link_to_older(cache_header.newest_active_block(), + free_block->link_to_older(cache_header.newest_block(), bin_header.active_block()); cache_header.register_new_block(free_block); bin_header.update_active_block(free_block, 0); @@ -730,7 +738,7 @@ class object_cache { // Make sure that the cache has enough space to allocate objects. while (total_size + new_objects_size > k_max_per_cpu_cache_size || free_blocks.empty()) { - auto *const oldest_block = cache_header.oldest_active_block(); + auto *const oldest_block = cache_header.oldest_block(); assert(oldest_block); // Deallocate objects from the oldest block @@ -762,7 +770,7 @@ class object_cache { std::unique_ptr m_cache{nullptr}; }; -// const_bin_iterator +/// An iterator to iterate over cached objects of the same bin. template class object_cache<_size_type, _difference_type, _bin_no_manager, From ce90027aa2fc9e883c8bcbbbdd573103927354fc Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Tue, 28 Nov 2023 10:13:15 -0800 Subject: [PATCH 11/26] Add code for running Sanitizers --- example/CMakeLists.txt | 2 + example/concurrent.cpp | 47 ++++++++++++++++++++ example/concurrent_map.cpp | 6 ++- scripts/sanitizer/run_sanitizers.sh | 69 +++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 example/concurrent.cpp create mode 100644 scripts/sanitizer/run_sanitizers.sh diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index fff4edf5..55997163 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -36,6 +36,8 @@ add_metall_executable(allocator_aware_type allocator_aware_type.cpp) add_metall_executable(logger logger.cpp) +add_metall_executable(concurrent concurrent.cpp) + if (BUILD_C) add_c_executable(c_api c_api.c) target_link_libraries(c_api PRIVATE metall_c) diff --git a/example/concurrent.cpp b/example/concurrent.cpp new file mode 100644 index 00000000..765499f2 --- /dev/null +++ b/example/concurrent.cpp @@ -0,0 +1,47 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +/// \file concurrent.cpp +/// \brief This example demonstrates Metall's concurrency support. +/// Metall can be used in a multi-threaded environment. +/// Please see the API documentation of the manager class to find out which +/// functions are thread-safe. + +#include +#include + +#include + +void metall_alloc(metall::manager& manager, const int tid) { + for (int i = 0; i < 10; ++i) { + if (tid % 2 == 0) { + manager.deallocate(manager.allocate(10)); + } else { + manager.destroy_ptr(manager.construct(metall::anonymous_instance)()); + } + } +} + +int main() { + { + metall::manager manager(metall::create_only, "/tmp/datastore"); + + { + std::thread t1(metall_alloc, std::ref(manager), 1); + std::thread t2(metall_alloc, std::ref(manager), 2); + std::thread t3(metall_alloc, std::ref(manager), 3); + std::thread t4(metall_alloc, std::ref(manager), 4); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } + assert(manager.check_sanity()); + assert(manager.all_memory_deallocated()); + } + + return 0; +} \ No newline at end of file diff --git a/example/concurrent_map.cpp b/example/concurrent_map.cpp index bbaa527c..383f66e5 100644 --- a/example/concurrent_map.cpp +++ b/example/concurrent_map.cpp @@ -1,6 +1,8 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. // -// Created by Iwabuchi, Keita on 9/25/20. -// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + #include #include diff --git a/scripts/sanitizer/run_sanitizers.sh b/scripts/sanitizer/run_sanitizers.sh new file mode 100644 index 00000000..1a6acf12 --- /dev/null +++ b/scripts/sanitizer/run_sanitizers.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# This script builds and runs example programs with various sanitizers. +# Usage example: +# cd metall # Metall root directory +# sh scripts/sanitizer/run_sanitizers.sh + +# Change the following variables to your own settings +CC=clang +CXX=clang++ +BOOST_ROOT="${HOME}/local/boost" +METALL_ROOT=".." + +LOG_FILE=out-sanitizer.log + +exec_cmd () { + echo "" >> ${LOG_FILE} + echo "------------------------------------------------------------" >> ${LOG_FILE} + echo "Command: " "$@" | tee -a ${LOG_FILE} + "$@" >> ${LOG_FILE} 2>&1 + echo "------------------------------------------------------------" >> ${LOG_FILE} + echo "" >> ${LOG_FILE} +} + +build () { + local target=$1 + exec_cmd ${CXX} -I ${BOOST_ROOT} -I ${METALL_ROOT}/include -std=c++17 ${SANITIZER_BUILD_ARGS} ${target} -DMETALL_DEFAULT_CAPACITY=$((1024*1024*500)) +} + +run_sanitizer () { + SANITIZER_BUILD_ARGS=$1 + +# build "${METALL_ROOT}/example/simple.cpp" +# ./a.out 2>&1 | tee -a ${LOG_FILE} +# rm -f a.out + + build "${METALL_ROOT}/example/concurrent.cpp" + echo "Running concurrent program" + ./a.out 2>&1 | tee -a ${LOG_FILE} + rm -f a.out +} + +main() { + mkdir build + cd build + rm -rf ${LOG_FILE} + + echo "" + echo "=====================================================================" + echo "Build with address sanitizer and leak sanitizer" + run_sanitizer "-fsanitize=address -fno-omit-frame-pointer -O1 -g" + + echo "" + echo "=====================================================================" + echo "Build with thread sanitizer" + run_sanitizer "-fsanitize=thread -fPIE -pie -O1 -g" + + echo "" + echo "=====================================================================" + echo "Build with memory sanitizer" + run_sanitizer "-fsanitize=memory -fsanitize-memory-track-origins -fPIE -pie -fno-omit-frame-pointer -g -O2" + + echo "" + echo "=====================================================================" + echo "Build with undefined behavior sanitizer" + run_sanitizer "-fsanitize=undefined -O1 -g" +} + +main "$@" \ No newline at end of file From 08886d25af28fd35074320da9479a93d738efa92 Mon Sep 17 00:00:00 2001 From: iwabuchi Date: Wed, 29 Nov 2023 20:01:24 -0800 Subject: [PATCH 12/26] Major refactoring in segment storage for custom segment storage Add Priavateer segment storage Update Umap SparseStore segment storage --- CMakeLists.txt | 65 +- include/metall/basic_manager.hpp | 152 +-- include/metall/defs.hpp | 2 +- include/metall/detail/file.hpp | 8 +- include/metall/detail/mmap.hpp | 5 +- include/metall/ext/privateer.hpp | 453 +++++++++ include/metall/ext/umap.hpp | 380 ++++++++ include/metall/kernel/manager_kernel.hpp | 198 ++-- include/metall/kernel/manager_kernel_fwd.hpp | 5 +- include/metall/kernel/manager_kernel_impl.ipp | 884 +++++++----------- .../kernel/manager_kernel_profile_impl.ipp | 5 +- include/metall/kernel/segment_header.hpp | 3 +- ...egment_storage.hpp => segment_storage.hpp} | 511 ++++++---- include/metall/kernel/storage.hpp | 82 ++ include/metall/metall.hpp | 15 + include/metall/stl_allocator.hpp | 2 +- test/container/stl_allocator_test.cpp | 4 +- test/kernel/CMakeLists.txt | 4 +- ..._file_test.cpp => copy_datastore_test.cpp} | 13 + test/kernel/manager_multithread_test.cpp | 34 +- test/kernel/mmap_segment_storage_test.cpp | 228 ----- test/kernel/multimanager_test.cpp | 11 +- test/kernel/segment_storage_test.cpp | 179 ++++ 23 files changed, 2044 insertions(+), 1199 deletions(-) create mode 100644 include/metall/ext/privateer.hpp create mode 100644 include/metall/ext/umap.hpp rename include/metall/kernel/{segment_storage/mmap_segment_storage.hpp => segment_storage.hpp} (59%) create mode 100644 include/metall/kernel/storage.hpp rename test/kernel/{copy_file_test.cpp => copy_datastore_test.cpp} (85%) delete mode 100644 test/kernel/mmap_segment_storage_test.cpp create mode 100644 test/kernel/segment_storage_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b818fa77..d98cd35a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,13 @@ include(FetchContent) # -------------------------------------------------------------------------------- # # CMake policy # -------------------------------------------------------------------------------- # -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") cmake_policy(SET CMP0077 NEW) -endif() +endif () -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") cmake_policy(SET CMP0135 NEW) -endif() +endif () # -------------------------------------------------------------------------------- # # Metall general configuration @@ -92,6 +92,7 @@ set(COMPILER_DEFS "" CACHE STRING "A list of Metall compile definitions to be ad # ---------- Experimental options ---------- # set(UMAP_ROOT "" CACHE PATH "UMap installed root directory") +set(PRIVATEER_ROOT "" CACHE PATH "Privateer installed root directory") option(ONLY_DOWNLOAD_GTEST "Only downloading Google Test" OFF) option(SKIP_DOWNLOAD_GTEST "Skip downloading Google Test" OFF) @@ -113,11 +114,11 @@ endif () # -------------------------------------------------------------------------------- # if (INSTALL_HEADER_ONLY) message(WARNING "INSTALL_HEADER_ONLY option has been replaced with JUST_INSTALL_METALL_HEADER.") -endif() +endif () if (JUST_INSTALL_METALL_HEADER) return() -endif() +endif () # -------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------- # @@ -151,9 +152,9 @@ if (NOT RUN_BUILD_AND_TEST_WITH_CI) endif () # ---------- Metall Macros ---------- # -foreach(X ${COMPILER_DEFS}) +foreach (X ${COMPILER_DEFS}) message(STATUS "Metall compile definition: ${X}") -endforeach() +endforeach () # ---------- CMAKE_BUILD_TYPE ---------- # @@ -183,6 +184,11 @@ if (UMAP_ROOT) endif () endif () +# ---------- Privateer ---------- # +if (PRIVATEER_ROOT) + message(STATUS "Privateer Root is: ${PRIVATEER_ROOT}") + find_library(LIBPRIVATEER NAMES privateer PATHS ${PRIVATEER_ROOT}/lib) +endif () # ---------- Boost ---------- # # Disable the boost-cmake feature (BoostConfig.cmake or boost-config.cmake) since @@ -246,9 +252,9 @@ function(common_setup_for_metall_executable name) # -------------------- # ----- Compile Definitions ----- # - foreach(X ${COMPILER_DEFS}) - target_compile_definitions(${name} PRIVATE ${X}) - endforeach() + foreach (X ${COMPILER_DEFS}) + target_compile_definitions(${name} PRIVATE ${X}) + endforeach () # -------------------- # ----- CXX17 Filesystem Lib----- # @@ -256,10 +262,10 @@ function(common_setup_for_metall_executable name) if (FOUND_CXX17_FILESYSTEM_LIB) if (REQUIRE_LIB_STDCXX_FS) target_link_libraries(${name} PRIVATE stdc++fs) - endif() - elseif() + endif () + elseif () target_compile_definitions(${name} PRIVATE "METALL_DISABLE_CXX17_FILESYSTEM_LIB") - endif() + endif () # -------------------- # ----- Umap----- # @@ -271,6 +277,37 @@ function(common_setup_for_metall_executable name) endif () endif () # -------------------- + + # ----- Privateer----- # + if (PRIVATEER_ROOT) + target_include_directories(${name} PRIVATE ${PRIVATEER_ROOT}/include) + if (LIBPRIVATEER) + # 1) Privateer Dependencies + FIND_PACKAGE(OpenSSL) + if (OpenSSL_FOUND) + target_link_libraries(${name} PRIVATE OpenSSL::SSL) + target_link_libraries(${name} PRIVATE OpenSSL::Crypto) + endif () + target_link_libraries(${name} PRIVATE rt) + FIND_PACKAGE(OpenMP REQUIRED) + if (OpenMP_CXX_FOUND) + target_link_libraries(${name} PRIVATE OpenMP::OpenMP_CXX) + else () + message(FATAL_ERROR "OpenMP is required to build Metall with Privateer") + endif () + if (ZSTD_ROOT) + find_library(LIBZSTD NAMES zstd PATHS ${ZSTD_ROOT}/lib) + target_include_directories(${name} PRIVATE ${ZSTD_ROOT}/lib) + target_link_libraries(${name} PRIVATE ${LIBZSTD}) + target_compile_definitions(${name} PRIVATE USE_COMPRESSION) + endif () + + # 2) Link Privateer + target_link_libraries(${name} PRIVATE ${LIBPRIVATEER}) + target_compile_definitions(${name} PRIVATE METALL_USE_PRIVATEER) + endif () + endif () + # -------------------- endfunction() function(add_metall_executable name source) diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index fb18ca9f..fd45270f 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -14,19 +14,27 @@ #include #include #include +#include +#include +#include namespace metall { #if !defined(DOXYGEN_SKIP) // Forward declaration -template +template class basic_manager; #endif // DOXYGEN_SKIP /// \brief A generalized Metall manager class -/// \tparam chunk_no_type -/// \tparam k_chunk_size -template class basic_manager { public: @@ -36,7 +44,8 @@ class basic_manager { /// \brief Manager kernel type using manager_kernel_type = - kernel::manager_kernel; + kernel::manager_kernel; /// \brief Void pointer type using void_pointer = typename manager_kernel_type::void_pointer; @@ -101,13 +110,17 @@ class basic_manager { /// \brief Chunk number type (= chunk_no_type) using chunk_number_type = chunk_no_type; + /// \brief Path type + using path_type = typename manager_kernel_type::path_type; + private: // -------------------- // // Private types and static values // -------------------- // using char_ptr_holder_type = typename manager_kernel_type::char_ptr_holder_type; - using self_type = basic_manager; + using self_type = + basic_manager; public: // -------------------- // @@ -116,7 +129,7 @@ class basic_manager { /// \brief Opens an existing data store. /// \param base_path Path to a data store. - basic_manager(open_only_t, const char *base_path) noexcept { + basic_manager(open_only_t, const path_type &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open(base_path); @@ -130,7 +143,7 @@ class basic_manager { /// \brief Opens an existing data store with the read only mode. /// Write accesses will cause segmentation fault. /// \param base_path Path to a data store. - basic_manager(open_read_only_t, const char *base_path) noexcept { + basic_manager(open_read_only_t, const path_type &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open_read_only(base_path); @@ -143,7 +156,7 @@ class basic_manager { /// \brief Creates a new data store (an existing data store will be /// overwritten). \param base_path Path to create a data store. - basic_manager(create_only_t, const char *base_path) noexcept { + basic_manager(create_only_t, const path_type &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->create(base_path); @@ -155,9 +168,13 @@ class basic_manager { } /// \brief Creates a new data store (an existing data store will be - /// overwritten). \param base_path Path to create a data store. \param - /// capacity Maximum total allocation size. - basic_manager(create_only_t, const char *base_path, + /// overwritten). + /// \param base_path Path to create a data store. + /// \param capacity Total allocation size. Metall uses this value as a hint. + // The actual limit could be smaller or larger than this value, depending on + // the internal implementation. However, a close minimum capacity should be + // available. + basic_manager(create_only_t, const path_type &base_path, const size_type capacity) noexcept { try { m_kernel = std::make_unique(); @@ -935,19 +952,18 @@ class basic_manager { /// \brief Takes a snapshot of the current data. The snapshot has a new UUID. /// \copydoc doc_single_thread /// - /// \param destination_dir_path Path to store a snapshot. + /// \param destination_path Path to store a snapshot. /// \param clone Use the file clone mechanism (reflink) instead of normal copy /// if it is available. \param num_max_copy_threads The maximum number of copy /// threads to use. If <= 0 is given, the value is automatically determined. /// \return Returns true on success; other false. - bool snapshot(const char_type *destination_dir_path, const bool clone = true, + bool snapshot(const path_type &destination_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { if (!check_sanity()) { return false; } try { - return m_kernel->snapshot(destination_dir_path, clone, - num_max_copy_threads); + return m_kernel->snapshot(destination_path, clone, num_max_copy_threads); } catch (...) { m_kernel.reset(nullptr); logger::out(logger::level::error, __FILE__, __LINE__, @@ -962,19 +978,18 @@ class basic_manager { /// \copydoc doc_thread_safe /// \details Copying to the same path simultaneously is prohibited. /// - /// \param source_dir_path Source data store path.\param - /// destination_dir_path Destination data store path. \param clone Use the - /// file clone mechanism (reflink) instead of normal copy if it is available. - /// \param num_max_copy_threads The maximum number of copy threads to use. - /// If <= 0 is given, the value is automatically determined. + /// \param source_path Source data store path. + /// \param destination_path Destination data store path. + /// \param clone Use the file clone mechanism (reflink) instead of normal copy + /// if it is available. \param num_max_copy_threads The maximum number of copy + /// threads to use. If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns true; other false. - static bool copy(const char_type *source_dir_path, - const char_type *destination_dir_path, - const bool clone = true, + static bool copy(const path_type &source_path, + const path_type &destination_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { - return manager_kernel_type::copy(source_dir_path, destination_dir_path, - clone, num_max_copy_threads); + return manager_kernel_type::copy(source_path, destination_path, clone, + num_max_copy_threads); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -988,20 +1003,21 @@ class basic_manager { /// \copydoc doc_thread_safe /// \details Copying to the same path simultaneously is prohibited. /// - /// \param source_dir_path Source data store path. \param - /// destination_dir_path Destination data store path. \param clone Use the - /// file clone mechanism (reflink) instead of normal copy if it is available. - /// \param num_max_copy_threads The maximum number of copy threads to use. - /// If <= 0 is given, the value is automatically determined. - /// \return Returns an object of std::future. - /// If succeeded, its get() returns true; other false. - static auto copy_async(const char_type *source_dir_path, - const char_type *destination_dir_path, + /// \param source_path Source data store path. + /// \param destination_path Destination data store path. + /// \param clone Use the file clone mechanism (reflink) instead of normal copy + /// if it is available. + /// \param num_max_copy_threads The maximum number of copy threads to use. If + /// <= 0 is given, the value is automatically determined. + /// \return Returns an object of std::future. If succeeded, its get() returns + /// true; other false. + static auto copy_async(const path_type source_path, + const path_type destination_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { - return manager_kernel_type::copy_async( - source_dir_path, destination_dir_path, clone, num_max_copy_threads); + return manager_kernel_type::copy_async(source_path, destination_path, + clone, num_max_copy_threads); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1013,11 +1029,11 @@ class basic_manager { /// \copydoc doc_thread_safe /// \details Must not remove the same data store simultaneously. /// - /// \param dir_path Path to a data store to remove. \return If + /// \param path Path to a data store to remove. \return If /// succeeded, returns true; other false. - static bool remove(const char_type *dir_path) noexcept { + static bool remove(const path_type &path) noexcept { try { - return manager_kernel_type::remove(dir_path); + return manager_kernel_type::remove(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1029,12 +1045,12 @@ class basic_manager { /// \copydoc doc_thread_safe /// \details Must not remove the same data store simultaneously. /// - /// \param dir_path Path to a data store to remove. + /// \param path Path to the data store to remove. /// \return Returns an object of std::future. /// If succeeded, its get() returns true; other false - static std::future remove_async(const char_type *dir_path) noexcept { + static std::future remove_async(const path_type &path) noexcept { try { - return std::async(std::launch::async, remove, dir_path); + return std::async(std::launch::async, remove, path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1051,12 +1067,12 @@ class basic_manager { /// read-only mode is undefined. /// If the data store is not consistent, it is recommended to remove /// the data store and create a new one. - /// \param dir_path Path to a data store. + /// \param path Path to a data store. /// \return Returns true if it exists and is consistent; otherwise, returns /// false. - static bool consistent(const char_type *dir_path) noexcept { + static bool consistent(const path_type &path) noexcept { try { - return manager_kernel_type::consistent(dir_path); + return manager_kernel_type::consistent(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1084,11 +1100,11 @@ class basic_manager { /// \brief Returns a UUID of the data store. /// \copydoc doc_thread_safe /// - /// \param dir_path Path to a data store. + /// \param path Path to a data store. /// \return UUID in the std::string format; returns an empty string on error. - static std::string get_uuid(const char_type *dir_path) noexcept { + static std::string get_uuid(const path_type &path) noexcept { try { - return manager_kernel_type::get_uuid(dir_path); + return manager_kernel_type::get_uuid(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1116,11 +1132,11 @@ class basic_manager { /// \brief Gets the version of the Metall that created the backing data store. /// \copydoc doc_thread_safe /// - /// \param dir_path Path to a data store. + /// \param path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char_type *dir_path) noexcept { + static version_type get_version(const path_type &path) noexcept { try { - return manager_kernel_type::get_version(dir_path); + return manager_kernel_type::get_version(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1158,13 +1174,13 @@ class basic_manager { /// store). /// \copydoc doc_const_datastore_thread_safe /// - /// \param dir_path Path to a data store. \param description An std::string + /// \param path Path to a data store. \param description An std::string /// object that holds a description. \return Returns true on success; /// otherwise, false. - static bool set_description(const char *dir_path, + static bool set_description(const path_type &path, const std::string &description) noexcept { try { - return manager_kernel_type::set_description(dir_path, description); + return manager_kernel_type::set_description(path, description); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1199,14 +1215,14 @@ class basic_manager { /// object. /// \copydoc doc_const_datastore_thread_safe /// - /// \param dir_path Path to a data store. \param description A pointer + /// \param path Path to a data store. \param description A pointer /// to an std::string object to store a description if it exists. \return /// Returns true on success; returns false on error. Trying to get a /// non-existent description is not considered as an error. - static bool get_description(const char *dir_path, + static bool get_description(const path_type &path, std::string *description) noexcept { try { - return manager_kernel_type::get_description(dir_path, description); + return manager_kernel_type::get_description(path, description); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1219,12 +1235,12 @@ class basic_manager { /// objects. /// \copydoc doc_object_attrb_obj_const_thread_safe /// - /// \param dir_path Path to a data store. \return Returns an instance + /// \param path Path to a data store. \return Returns an instance /// of named_object_attribute_accessor_type. static named_object_attribute_accessor_type access_named_object_attribute( - const char *dir_path) noexcept { + const path_type &path) noexcept { try { - return manager_kernel_type::access_named_object_attribute(dir_path); + return manager_kernel_type::access_named_object_attribute(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1236,12 +1252,12 @@ class basic_manager { /// object. /// \copydoc doc_object_attrb_obj_const_thread_safe /// - /// \param dir_path Path to a data store. \return Returns an instance + /// \param path Path to a data store. \return Returns an instance /// of unique_object_attribute_accessor_type. static unique_object_attribute_accessor_type access_unique_object_attribute( - const char *dir_path) noexcept { + const path_type &path) noexcept { try { - return manager_kernel_type::access_unique_object_attribute(dir_path); + return manager_kernel_type::access_unique_object_attribute(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1253,12 +1269,12 @@ class basic_manager { /// anonymous object. /// \copydoc doc_object_attrb_obj_const_thread_safe /// - /// \param dir_path Path to a data store. \return Returns an + /// \param path Path to a data store. \return Returns an /// instance of anonymous_object_attribute_accessor_type. static anonymous_object_attribute_accessor_type - access_anonymous_object_attribute(const char *dir_path) noexcept { + access_anonymous_object_attribute(const path_type &path) noexcept { try { - return manager_kernel_type::access_anonymous_object_attribute(dir_path); + return manager_kernel_type::access_anonymous_object_attribute(path); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); @@ -1279,7 +1295,7 @@ class basic_manager { } try { return allocator_type(reinterpret_cast( - &(m_kernel->get_segment_header()->manager_kernel_address))); + &(m_kernel->get_segment_header().manager_kernel_address))); } catch (...) { logger::out(logger::level::error, __FILE__, __LINE__, "An exception has been thrown"); diff --git a/include/metall/defs.hpp b/include/metall/defs.hpp index 0d5604ed..f8385e72 100644 --- a/include/metall/defs.hpp +++ b/include/metall/defs.hpp @@ -49,7 +49,7 @@ #endif // -------------------- -// Macros for the default segment storage manager +// Macros for the default segment storage // -------------------- /// \def METALL_SEGMENT_BLOCK_SIZE diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index f10a999e..e987d38c 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -220,7 +220,9 @@ inline bool create_file(const std::string &file_path) { const int fd = ::open(file_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd == -1) { - logger::perror(logger::level::error, __FILE__, __LINE__, "open"); + std::stringstream ss; + ss << "Failed to create: " << file_path; + logger::perror(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } @@ -538,8 +540,10 @@ inline bool copy_files_in_directory_in_parallel_helper( ©_func) { std::vector src_file_names; if (!get_regular_file_names(source_dir_path, &src_file_names)) { + std::stringstream ss; + ss << "Failed to get file list in " << source_dir_path; logger::out(logger::level::error, __FILE__, __LINE__, - "Failed to get file list"); + ss.str().c_str()); return false; } diff --git a/include/metall/detail/mmap.hpp b/include/metall/detail/mmap.hpp index 8f14e7ed..5e244c67 100644 --- a/include/metall/detail/mmap.hpp +++ b/include/metall/detail/mmap.hpp @@ -250,8 +250,9 @@ inline bool munmap(const int fd, void *const addr, const size_t length, } inline bool map_with_prot_none(void *const addr, const size_t length) { - return (os_mmap(addr, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, - 0) == addr); + auto flag = MAP_PRIVATE | MAP_ANONYMOUS; + if (addr != nullptr) flag |= MAP_FIXED; + return (os_mmap(addr, length, PROT_NONE, flag, -1, 0) == addr); } inline bool os_mprotect(void *const addr, const size_t length, const int prot) { diff --git a/include/metall/ext/privateer.hpp b/include/metall/ext/privateer.hpp new file mode 100644 index 00000000..ef286f90 --- /dev/null +++ b/include/metall/ext/privateer.hpp @@ -0,0 +1,453 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#pragma once + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace metall { + +namespace { +namespace mdtl = metall::mtlldetail; +} + +class privateer_storage; +class privateer_segment_storage; + +/// \brief Metall manager with Privateer. +using manager_privateer = + basic_manager; + +#ifdef METALL_USE_PRIVATEER +using manager = manager_privateer; +#endif + +class privateer_storage : public metall::kernel::storage { + public: + using path_type = std::filesystem::path; + + static path_type get_path(const path_type &raw_path, const path_type &key) { + return get_path(raw_path, {key}); + } + + static path_type get_path(const path_type &raw_path, + const std::initializer_list &subpaths) { + auto root_path = priv_get_root_path(raw_path.string()); + for (const auto &p : subpaths) { + root_path /= p; + } + return root_path; + } + + private: + static path_type priv_get_root_path(const std::string &raw_path) { + std::string base_dir = ""; + size_t stash_path_index = raw_path.find(""); + if (stash_path_index != std::string::npos) { + base_dir = raw_path.substr((stash_path_index + 7), + raw_path.length() - (stash_path_index + 7)); + } else { + base_dir = raw_path; + } + return base_dir; + } +}; + +class privateer_segment_storage { + public: + using path_type = privateer_storage::path_type; + using segment_header_type = metall::kernel::segment_header; + + privateer_segment_storage() { priv_load_system_page_size(); } + + ~privateer_segment_storage() { + priv_sync_segment(true); + release(); + } + + privateer_segment_storage(const privateer_segment_storage &) = delete; + privateer_segment_storage &operator=(const privateer_segment_storage &) = + delete; + + privateer_segment_storage(privateer_segment_storage &&other) noexcept + : m_system_page_size(other.m_system_page_size), + m_vm_region_size(other.m_vm_region_size), + m_current_segment_size(other.m_current_segment_size), + m_vm_region(other.m_vm_region), + m_segment(other.m_segment), + m_segment_header(other.m_segment_header), + m_base_path(other.m_base_path), + m_read_only(other.m_read_only), + m_privateer(other.m_privateer), + m_privateer_version_name(other.m_privateer_version_name) { + other.priv_reset(); + } + + privateer_segment_storage &operator=( + privateer_segment_storage &&other) noexcept { + m_system_page_size = std::move(other.m_system_page_size); + m_vm_region_size = std::move(other.m_vm_region_size); + m_current_segment_size = std::move(other.m_current_segment_size); + m_vm_region = std::move(other.m_vm_region); + m_segment = std::move(other.m_segment); + m_segment_header = std::move(other.m_segment_header); + m_base_path = std::move(other.m_base_path); + m_read_only = std::move(other.m_read_only); + m_privateer = std::move(other.m_privateer); + m_privateer_version_name = std::move(other.m_privateer_version_name); + + other.priv_reset(); + + return (*this); + } + + static bool copy(const std::string &source_path, + const std::string &destination_path, + [[maybe_unused]] const bool clone, + const int max_num_threads) { + if (!mtlldetail::copy_files_in_directory_in_parallel( + parse_path(source_path).first, parse_path(destination_path).first, + max_num_threads)) { + return false; + } + + return true; + } + + bool snapshot(std::string destination_path, const bool clone, + const int max_num_threads) { + sync(true); + auto path = parse_path(destination_path).first; + std::pair parsed_path = priv_parse_path(path); + std::string version_path = parsed_path.second; + if (!m_privateer->snapshot(version_path.c_str())) { + return false; + } + if (!copy(m_base_path, path, clone, max_num_threads)) { + return false; + } + return true; + } + + bool create(const path_type &base_path, const std::size_t capacity) { + assert(!priv_inited()); + init_privateer_datastore(base_path.string(), Privateer::CREATE); + m_base_path = parse_path(base_path).first; + const auto header_size = priv_aligned_header_size(); + const auto vm_region_size = header_size + capacity; + if (!priv_reserve_vm(vm_region_size)) { + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + priv_construct_segment_header(m_vm_region); + m_read_only = false; + + const auto segment_size = vm_region_size - header_size; + + if (!priv_create_and_map_file(m_base_path, segment_size, m_segment)) { + priv_reset(); + return false; + } + return true; + } + + bool open(const std::string &base_path, const std::size_t, + const bool read_only) { + assert(!priv_inited()); + init_privateer_datastore(base_path, Privateer::OPEN); + + m_base_path = parse_path(base_path).first; + m_read_only = read_only; + + const auto header_size = priv_aligned_header_size(); + const auto segment_size = Privateer::version_capacity(m_base_path.c_str()); + const auto vm_size = header_size + segment_size; + if (!priv_reserve_vm(vm_size)) { + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + priv_construct_segment_header(m_vm_region); + + if (!priv_map_file_open(m_base_path, static_cast(m_segment), + read_only)) { // , store)) { + std::abort(); // Fatal error + } + + return true; + } + + bool extend(const std::size_t) { + // TODO: check errors + return true; + } + + void init_privateer_datastore(std::string path, int action) { + const std::lock_guard lock(m_create_mutex); + std::pair base_stash_pair = parse_path(path); + std::string base_dir = base_stash_pair.first; + std::string stash_dir = base_stash_pair.second; + std::pair parsed_path = priv_parse_path(base_dir); + std::string privateer_base_path = parsed_path.first; + std::string version_path = parsed_path.second; + m_privateer_version_name = version_path; + /* int action = + std::filesystem::exists(std::filesystem::path(privateer_base_path)) + ? Privateer::OPEN + : Privateer::CREATE; */ + if (!stash_dir.empty()) { + m_privateer = + new Privateer(action, privateer_base_path.c_str(), stash_dir.c_str()); + } else { + m_privateer = new Privateer(action, privateer_base_path.c_str()); + } + if (action == Privateer::CREATE) { + m_privateer_block_size = m_privateer->get_block_size(); + } else { + std::string version_full_path = privateer_base_path + "/" + version_path; + m_privateer_block_size = Privateer::version_block_size(version_full_path); + } + } + + static std::pair parse_path(std::string path) { + std::string base_dir = ""; + std::string stash_dir = ""; + size_t stash_path_index = path.find(""); + if (stash_path_index != std::string::npos) { + stash_dir = path.substr(0, stash_path_index); + base_dir = path.substr((stash_path_index + 7), + path.length() - (stash_path_index + 7)); + } else { + base_dir = path; + } + return std::pair(base_dir, stash_dir); + } + + void release() { priv_release(); } + + void sync(const bool sync) { priv_sync_segment(sync); } + + void free_region(const std::ptrdiff_t, const std::size_t) { + // Do nothing + // Privateer does not free file region + } + + void *get_segment() const { return m_segment; } + + segment_header_type &get_segment_header() { + return *reinterpret_cast(m_vm_region); + } + + const segment_header_type &get_segment_header() const { + return *reinterpret_cast(m_vm_region); + } + + std::size_t size() const { return m_current_segment_size; } + + std::size_t page_size() const { return m_system_page_size; } + + bool read_only() const { return m_read_only; } + + bool is_open() const { return !!m_privateer; } + + bool check_sanity() const { + // TODO: implement + return true; + } + + private: + std::size_t priv_aligment() const { + // FIXME + // return 1 << 28; + if (m_system_page_size < m_privateer_block_size) { + return mdtl::round_up(int64_t(m_privateer_block_size), + int64_t(m_system_page_size)); + } else { + return mdtl::round_up(int64_t(m_system_page_size), + int64_t(m_privateer_block_size)); + } + } + + void priv_reset() { + m_vm_region_size = 0; + m_current_segment_size = 0; + m_vm_region = nullptr; + m_segment = nullptr; + m_segment_header = nullptr; + m_base_path.clear(); + m_privateer = nullptr; + m_privateer_version_name.clear(); + } + + bool priv_inited() const { + if (m_privateer) { + assert(m_system_page_size > 0 && m_vm_region_size > 0 && + m_current_segment_size > 0 && m_segment && !m_base_path.empty()); + return true; + } + return false; + } + + bool priv_create_and_map_file(const std::string &file_name, + const std::size_t file_size, void *const addr) { + assert(!m_segment || + static_cast(m_segment) + m_current_segment_size <= addr); + + if (!priv_map_file_create(file_name, file_size, addr)) { + return false; + } + return true; + } + + bool priv_map_file_create(const std::string &path, + const std::size_t file_size, void *const addr) { + assert(!path.empty()); + assert(file_size > 0); + assert(addr); + void *data = m_privateer->create(addr, m_privateer_version_name.c_str(), + file_size, true); + if (data == nullptr) { + return false; + } + + m_current_segment_size = file_size; // privateer->current_size(); + return true; + } + + bool priv_map_file_open(const std::string &path, void *const addr, + const bool read_only) { + assert(!path.empty()); + assert(addr); + + void *data = + read_only ? m_privateer->open_read_only( + addr, m_privateer_version_name.c_str()) + : m_privateer->open(addr, m_privateer_version_name.c_str()); + m_current_segment_size = m_privateer->region_size(); + return true; + } + + bool priv_reserve_vm(const std::size_t nbytes) { + m_vm_region_size = + mdtl::round_up((int64_t)nbytes, (int64_t)priv_aligment()); + m_vm_region = + mdtl::reserve_aligned_vm_region(priv_aligment(), m_vm_region_size); + + if (!m_vm_region) { + std::stringstream ss; + ss << "Cannot reserve a VM region " << nbytes << " bytes"; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + m_vm_region_size = 0; + return false; + } + assert(reinterpret_cast(m_vm_region) % priv_aligment() == 0); + + return true; + } + + void priv_release() { + if (!priv_inited()) return; + + delete m_privateer; + m_privateer = nullptr; + + // another therad gets the vm region + + // Just erase segment header + mdtl::map_with_prot_none(m_vm_region, m_vm_region_size); + mdtl::munmap(m_vm_region, m_vm_region_size, false); + + priv_reset(); + } + + std::size_t priv_aligned_header_size() { + const auto size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + return size; + } + + bool priv_construct_segment_header(void *const addr) { + if (!addr) { + return false; + } + + const auto size = priv_aligned_header_size(); + if (mdtl::map_anonymous_write_mode(addr, size, MAP_FIXED) != addr) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Cannot allocate segment header"); + return false; + } + m_segment_header = reinterpret_cast(addr); + + new (m_segment_header) segment_header_type(); + + return true; + } + + void priv_sync_segment(const bool) { + if (!priv_inited() || m_read_only) return; + m_privateer->msync(); + } + + bool priv_load_system_page_size() { + m_system_page_size = mdtl::get_page_size(); + if (m_system_page_size == -1) { + logger::out(logger::level::critical, __FILE__, __LINE__, + "Failed to get system pagesize"); + return false; + } + return true; + } + + std::pair priv_parse_path(std::string path) { + std::pair parsed; + size_t position = 0; + std::string token = "/"; + position = path.find_last_of(token); + std::string privateer_base_path = path.substr(0, position); + std::string version_name = path.substr(position + 1, path.length()); + parsed = std::make_pair(privateer_base_path, version_name); + return parsed; + } + + ssize_t m_system_page_size{0}; + std::size_t m_privateer_block_size{0}; + std::size_t m_vm_region_size{0}; + std::size_t m_current_segment_size{0}; + void *m_vm_region{nullptr}; + void *m_segment{nullptr}; + segment_header_type *m_segment_header{nullptr}; + std::string m_base_path{}; + bool m_read_only{false}; + mutable Privateer *m_privateer{nullptr}; + std::string m_privateer_version_name{}; + std::mutex m_create_mutex{}; +}; + +} // namespace metall diff --git a/include/metall/ext/umap.hpp b/include/metall/ext/umap.hpp new file mode 100644 index 00000000..0821aa65 --- /dev/null +++ b/include/metall/ext/umap.hpp @@ -0,0 +1,380 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include +#include + +// UMap SparseStore file granularity +const size_t SPARSE_STORE_FILE_GRANULARITY_DEFAULT = 8388608L; + +namespace metall { + +namespace { +namespace mdtl = metall::mtlldetail; +} + +class umap_storage; +class umap_segment_storage; + +/// \brief Metall manager with UMap SparseStore. +using manager_umap = basic_manager; + +#ifdef METALL_USE_UMAP +using manager = manager_umap; +#endif + +class umap_storage : public metall::kernel::storage { + private: + using base_type = metall::kernel::storage; + + public: + using base_type::base_type; +}; + +class umap_segment_storage { + public: + using path_type = umap_storage::path_type; + using segment_header_type = metall::kernel::segment_header; + + umap_segment_storage() { + m_umap_page_size = ::umapcfg_get_umap_page_size(); + if (m_umap_page_size == -1) { + std::cerr << "Failed to get Umap pagesize" << std::endl; + std::abort(); + } + if (!priv_load_system_page_size()) { + std::abort(); + } + } + + ~umap_segment_storage() { + priv_sync_segment(true); + release(); + } + + umap_segment_storage(const umap_segment_storage &) = delete; + umap_segment_storage &operator=(const umap_segment_storage &) = delete; + + umap_segment_storage(umap_segment_storage &&other) noexcept {} + umap_segment_storage &operator=(umap_segment_storage &&other) noexcept {} + + static bool copy(const path_type &source_path, + const path_type &destination_path, + [[maybe_unused]] const bool clone, + const int max_num_threads) { + /*if (clone) { + std::string s("Clone: " + source_path.u8string()); + logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); + return mdtl::clone_file(source_path, destination_path); + } */ /* else { */ + + /* Create SparseStore Metadata dir */ + const auto sparse_store_source_path = + priv_make_file_name(source_path.u8string()); + const auto sparse_store_dst_path = + priv_make_file_name(destination_path.u8string()); + + if (!mdtl::create_directory(sparse_store_dst_path)) { + std::stringstream ss; + ss << "Failed to create directory: " << sparse_store_dst_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return false; + } + std::string s("Copy: " + source_path.u8string()); + logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); + if (!mdtl::copy_files_in_directory_in_parallel( + source_path, destination_path, max_num_threads)) { + return false; + } + + if (!mdtl::copy_files_in_directory_in_parallel( + sparse_store_source_path, sparse_store_dst_path, max_num_threads)) { + return false; + } + + return true; + /* } */ + /* assert(false); + return false; */ + } + + bool snapshot(path_type destination_path, const bool clone, + const int max_num_threads) { + sync(true); + if (!copy(m_base_path, destination_path, clone, max_num_threads)) { + return false; + } + return true; + } + + bool create(const path_type &base_path, const std::size_t capacity) { + assert(!priv_inited()); + + m_base_path = base_path; + + const auto header_size = priv_aligned_header_size(); + const auto vm_region_size = header_size + capacity; + if (!priv_reserve_vm(vm_region_size)) { + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + m_current_segment_size = vm_region_size - header_size; + priv_construct_segment_header(m_vm_region); + m_read_only = false; + + const auto segment_size = vm_region_size - header_size; + + // assert(!path.empty()); + // assert(file_size > 0); + // assert(addr); + + size_t sparse_file_granularity = priv_get_sparsestore_file_granularity(); + size_t page_size = umapcfg_get_umap_page_size(); + if (page_size == -1) { + ::perror("umapcfg_get_umap_page_size failed"); + std::cerr << "errno: " << errno << std::endl; + return false; + } + const std::string file_name = priv_make_file_name(base_path); + /* store = new Umap::SparseStore(segment_size, page_size, file_name, + page_size); */ + store = std::make_unique(segment_size, page_size, + file_name, page_size); + + const int prot = PROT_READ | PROT_WRITE; + const int flags = UMAP_PRIVATE | UMAP_FIXED; + m_segment = Umap::umap_ex(m_segment, (segment_size - m_umap_page_size), + prot, flags, -1, 0, store.get()); + if (m_segment == UMAP_FAILED) { + std::ostringstream ss; + ss << "umap_mf of " << segment_size << " bytes failed for " + << m_base_path; + perror(ss.str().c_str()); + return false; + } + return true; + } + + bool open(const path_type &base_path, const std::size_t, + const bool read_only) { + assert(!priv_inited()); + m_base_path = base_path; + + const auto header_size = priv_aligned_header_size(); + const auto directory_name = priv_make_file_name(base_path); + m_current_segment_size = Umap::SparseStore::get_capacity(directory_name); + m_vm_region_size = header_size + m_current_segment_size; + if (!priv_reserve_vm(m_vm_region_size)) { + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + priv_construct_segment_header(m_vm_region); + + m_read_only = read_only; + + size_t page_size = umapcfg_get_umap_page_size(); + if (page_size == -1) { + ::perror("umapcfg_get_umap_page_size failed"); + std::cerr << "errno: " << errno << std::endl; + } + + // store = new Umap::SparseStore(directory_name, read_only); + store = std::make_unique(directory_name, read_only); + const int prot = PROT_READ | (read_only ? 0 : PROT_WRITE); + const int flags = UMAP_PRIVATE | UMAP_FIXED; + m_segment = + Umap::umap_ex(m_segment, (m_current_segment_size - m_umap_page_size), + prot, flags, -1, 0, store.get()); + if (m_segment == UMAP_FAILED) { + std::ostringstream ss; + ss << "umap_mf of " << m_current_segment_size << " bytes failed for " + << directory_name; + perror(ss.str().c_str()); + return false; + } + return true; + } + + bool extend(const std::size_t) { /* TODO implement */ return true; } + + void release() { priv_release(); } + + void sync(const bool sync) { priv_sync_segment(sync); } + + void free_region(const std::ptrdiff_t, const std::size_t) { + /* not currently supported in Umap?? */ + } + + void *get_segment() const { return m_segment; } + + segment_header_type &get_segment_header() { + return *reinterpret_cast(m_vm_region); + } + + const segment_header_type &get_segment_header() const { + return *reinterpret_cast(m_vm_region); + } + + std::size_t size() const { return m_current_segment_size; } + + std::size_t page_size() const { + return m_system_page_size; // m_umap_page_size; + } + + bool read_only() const { return m_read_only; } + + bool is_open() const { return !!store; } + + bool check_sanity() const { /* TODO implement */ return true; } + + private: + bool priv_inited() const { + return (m_umap_page_size > 0 && m_vm_region_size > 0 && + m_current_segment_size > 0 && m_segment && !m_base_path.empty()); + } + + std::size_t priv_aligned_header_size() { + const auto size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + return size; + } + + std::size_t priv_aligment() const { + // FIXME + // return 1 << 28; + size_t result; + if (m_system_page_size < m_umap_page_size) { + /* return */ result = mdtl::round_up(int64_t(m_umap_page_size), + int64_t(m_system_page_size)); + } else { + /* return */ result = mdtl::round_up(int64_t(m_system_page_size), + int64_t(m_umap_page_size)); + } + return result; + } + + bool priv_construct_segment_header(void *const addr) { + if (!addr) { + return false; + } + + const auto size = priv_aligned_header_size(); + if (mdtl::map_anonymous_write_mode(addr, size, MAP_FIXED) != addr) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Cannot allocate segment header"); + return false; + } + m_segment_header = reinterpret_cast(addr); + + new (m_segment_header) segment_header_type(); + + return true; + } + + static std::string priv_make_file_name(const std::string &base_path) { + return base_path + "/umap_sparse_segment_file"; + } + + size_t priv_get_sparsestore_file_granularity() const { + char *file_granularity_str = getenv("SPARSE_STORE_FILE_GRANULARITY"); + size_t file_granularity; + if (file_granularity_str == NULL) { + file_granularity = SPARSE_STORE_FILE_GRANULARITY_DEFAULT; + } else { + file_granularity = (size_t)std::stol(file_granularity_str); + } + return file_granularity; + } + + bool priv_reserve_vm(const std::size_t nbytes) { + m_vm_region_size = + mdtl::round_up((int64_t)nbytes, (int64_t)priv_aligment()); + m_vm_region = + mdtl::reserve_aligned_vm_region(priv_aligment(), m_vm_region_size); + + if (!m_vm_region) { + std::stringstream ss; + ss << "Cannot reserve a VM region " << nbytes << " bytes"; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + m_vm_region_size = 0; + return false; + } + assert(reinterpret_cast(m_vm_region) % priv_aligment() == 0); + + return true; + } + + void priv_sync_segment(const bool) { + if (!priv_inited() || m_read_only) return; + if (::umap_flush() != 0) { + std::cerr << "Failed umap_flush()" << std::endl; + std::abort(); + } + } + + void priv_release() { + if (!priv_inited()) return; + + const auto file_name = priv_make_file_name(m_base_path); + assert(mdtl::file_exist(file_name)); + if (::uunmap(static_cast(m_segment), m_current_segment_size) != 0) { + std::cerr << "Failed to unmap a Umap region" << std::endl; + std::abort(); + } + m_current_segment_size = 0; + int sparse_store_close_files = + store.get()->close_files(); // store->close_files(); + if (sparse_store_close_files != 0) { + std::cerr << "Error closing SparseStore files" << std::endl; + std::abort(); + } + mdtl::map_with_prot_none(m_vm_region, m_vm_region_size); + mdtl::munmap(m_vm_region, m_vm_region_size, false); + + priv_reset(); + } + + void priv_reset() { + m_umap_page_size = 0; + m_vm_region_size = 0; + m_current_segment_size = 0; + m_segment = nullptr; + // m_read_only = false; + } + + bool priv_load_system_page_size() { + m_system_page_size = mdtl::get_page_size(); + if (m_system_page_size == -1) { + logger::out(logger::level::critical, __FILE__, __LINE__, + "Failed to get system pagesize"); + return false; + } + return true; + } + + size_t m_system_page_size{0}; + size_t m_umap_page_size{0}; + size_t m_current_segment_size{0}; + size_t m_vm_region_size{0}; + void *m_segment{nullptr}; + segment_header_type *m_segment_header{nullptr}; + std::string m_base_path{}; + void *m_vm_region{nullptr}; + bool m_read_only; + // mutable Umap::SparseStore *store; + std::unique_ptr store{nullptr}; +}; + +} // namespace metall diff --git a/include/metall/kernel/manager_kernel.hpp b/include/metall/kernel/manager_kernel.hpp index c7eb3614..9ec8d0d3 100644 --- a/include/metall/kernel/manager_kernel.hpp +++ b/include/metall/kernel/manager_kernel.hpp @@ -36,12 +36,6 @@ #include #include -#ifdef METALL_USE_UMAP -#include -#else -#include -#endif - #ifndef METALL_DISABLE_CONCURRENCY #define METALL_ENABLE_MUTEX_IN_MANAGER_KERNEL #endif @@ -56,7 +50,8 @@ namespace { namespace mdtl = metall::mtlldetail; } -template +template class manager_kernel { public: // -------------------- // @@ -78,10 +73,9 @@ class manager_kernel { // -------------------- // // Private types and static values // -------------------- // - using self_type = manager_kernel<_chunk_no_type, _chunk_size>; - static constexpr const char *k_datastore_top_dir_name = "metall_datastore"; - static constexpr const char *k_datastore_management_dir_name = "management"; - static constexpr const char *k_datastore_segment_dir_name = "segment"; + using self_type = + manager_kernel<_storage, _segment_storage, _chunk_no_type, _chunk_size>; + static constexpr const char *k_management_dir_name = "management"; // For segment #ifndef METALL_DEFAULT_CAPACITY @@ -99,33 +93,19 @@ class manager_kernel { static_assert(k_default_vm_reserve_size <= k_max_segment_size, "k_default_vm_reserve_size must be <= k_max_segment_size"); -#ifndef METALL_SEGMENT_BLOCK_SIZE -#error "METALL_SEGMENT_BLOCK_SIZE is not defined." -#endif - static constexpr size_type k_initial_segment_size = - METALL_SEGMENT_BLOCK_SIZE; - static_assert(k_initial_segment_size <= k_default_vm_reserve_size, - "k_initial_segment_size must be <= k_default_vm_reserve_size"); - static_assert(k_chunk_size <= k_initial_segment_size, - "Chunk size must be <= k_initial_segment_size"); - using segment_header_type = segment_header; static constexpr size_type k_segment_header_size = mdtl::round_up(sizeof(segment_header_type), k_chunk_size); - using segment_storage_type = -#ifdef METALL_USE_UMAP - umap_sparse_segment_storage; -#else - mmap_segment_storage; -#endif + using storage = _storage; + using segment_storage = _segment_storage; // For actual memory allocation layer static constexpr const char *k_segment_memory_allocator_prefix = "segment_memory_allocator"; using segment_memory_allocator = segment_allocator; + k_max_segment_size, segment_storage>; // For attributed object directory using attributed_object_directory_type = @@ -173,6 +153,7 @@ class manager_kernel { using anonymous_object_attr_accessor_type = anonymous_object_attr_accessor< attributed_object_directory_type::offset_type, attributed_object_directory_type::size_type>; + using path_type = typename storage::path_type; // -------------------- // // Constructor & assign operator @@ -192,25 +173,25 @@ class manager_kernel { // -------------------- // /// \brief Creates a new datastore /// Expect to be called by a single thread - /// \param base_dir_path + /// \param base_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool create(const char *base_dir_path, + bool create(const path_type &base_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore /// Expect to be called by a single thread - /// \param base_dir_path + /// \param base_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool open(const char *base_dir_path, + bool open(const path_type &base_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore with read only /// Expect to be called by a single thread - /// \param base_dir_path + /// \param base_path /// \return Returns true if success; otherwise, returns false - bool open_read_only(const char *base_dir_path); + bool open_read_only(const path_type &base_path); /// \brief Expect to be called by a single thread void close(); @@ -287,7 +268,7 @@ class manager_kernel { /// construct/find_or_construct functions (1 if is a single element, >=1 if /// it's an array), is T. \tparam T \param ptr \return template - bool is_instance_type(const void *const ptr) const; + bool is_instance_type(const void *ptr) const; /// \brief Gets the description of an object created with /// construct/find_or_construct. \tparam T The type of the object. \param ptr @@ -354,9 +335,9 @@ class manager_kernel { T *generic_construct(char_ptr_holder_type name, size_type num, bool try2find, bool do_throw, mdtl::in_place_interface &table); - /// \brief Get the address of the segment header. - /// \return Returns the address of the segment header. - const segment_header_type *get_segment_header() const; + /// \brief Return a reference to the segment header. + /// \return A reference to the segment header. + const segment_header_type &get_segment_header() const; /// \brief Get the address of the application segment segment. /// \return Returns the address of the application segment segment. @@ -367,72 +348,71 @@ class manager_kernel { size_type get_segment_size() const; /// \brief Takes a snapshot. The snapshot has a different UUID. - /// \param destination_dir_path Destination path + /// \param destination_base_path Destination path /// \param clone Use clone (reflink) to copy data. /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false - bool snapshot(const char *destination_dir_path, const bool clone, - const int num_max_copy_threads); + bool snapshot(const path_type &destination_base_path, bool clone, + int num_max_copy_threads); /// \brief Copies a data store synchronously, keeping the same UUID. - /// \param source_dir_path Source path. - /// \param destination_dir_path Destination path. + /// \param source_base_path Source path. + /// \param destination_base_path Destination path. /// \param clone Use clone (reflink) to copy data. /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false. - static bool copy(const char *source_dir_path, - const char *destination_dir_path, const bool clone, - const int num_max_copy_threads); + static bool copy(const path_type &source_base_path, + const path_type &destination_base_path, bool clone, + int num_max_copy_threads); /// \brief Copies a data store asynchronously, keeping the same UUID. - /// \param source_dir_path Source path. - /// \param destination_dir_path Destination path. + /// \param source_base_path Source path. + /// \param destination_base_path Destination path. /// \param clone Use clone (reflink) to copy data. /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return Returns an object of std::future. /// If succeeded, its get() returns True; other false. - static std::future copy_async(const char *source_dir_path, - const char *destination_dir_path, - const bool clone, - const int num_max_copy_threads); + static std::future copy_async(const path_type &source_base_path, + const path_type &destination_base_path, + bool clone, int num_max_copy_threads); /// \brief Remove a data store synchronously - /// \param base_dir_path + /// \param base_path /// \return If succeeded, returns True; other false - static bool remove(const char *base_dir_path); + static bool remove(const path_type &base_path); /// \brief Remove a data store asynchronously - /// \param base_dir_path + /// \param base_path /// \return Returns an object of std::future /// If succeeded, its get() returns True; other false - static std::future remove_async(const char *base_dir_path); + static std::future remove_async(const path_type &base_path); /// \brief Check if the backing data store is consistent, /// i.e. it was closed properly. /// \param dir_path /// \return Return true if it is consistent; otherwise, returns false. - static bool consistent(const char *dir_path); + static bool consistent(const path_type &dir_path); /// \brief Returns the UUID of the backing data store. /// \return Returns UUID in std::string; returns an empty string on error. std::string get_uuid() const; /// \brief Returns the UUID of the backing data store. - /// \param dir_path Path to a data store. + /// \param base_path Path to a data store. /// \return Returns UUID in std::string; returns an empty string on error. - static std::string get_uuid(const char *dir_path); + static std::string get_uuid(const path_type &base_path); /// \brief Gets the version number of the backing data store. /// \return Returns a version number; returns 0 on error. version_type get_version() const; /// \brief Gets the version number of the backing data store. - /// \param dir_path Path to a data store. + /// \param base_path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char *dir_path); + static version_type get_version(const path_type &base_path); /// \brief Gets a description from a file. /// \param description A pointer to a string buffer. @@ -440,10 +420,10 @@ class manager_kernel { bool get_description(std::string *description) const; /// \brief Gets a description from a file. - /// \param base_dir_path Path to a data store. + /// \param base_path Path to a data store. /// \param description A pointer to a string buffer. /// \return Returns false on error. - static bool get_description(const std::string &base_dir_path, + static bool get_description(const path_type &base_path, std::string *description); /// \brief Sets a description to a file. @@ -452,33 +432,29 @@ class manager_kernel { bool set_description(const std::string &description); /// \brief Sets a description to a file. - /// \param base_dir_path Path to a data store. + /// \param base_path Path to a data store. /// \param description A description to write. /// \return Returns false on error. - static bool set_description(const std::string &base_dir_path, + static bool set_description(const path_type &base_path, const std::string &description); /// \brief Returns an instance that provides access to the attribute of named - /// objects. \param base_dir_path Path to a data store. \return Returns an + /// objects. \param base_path Path to a data store. \return Returns an /// instance of named_object_attr_accessor_type. static named_object_attr_accessor_type access_named_object_attribute( - const std::string &base_dir_path); + const path_type &base_path); /// \brief Returns an instance that provides access to the attribute of unique - /// object. \param base_dir_path Path to a data store. \return Returns an + /// object. \param base_path Path to a data store. \return Returns an /// instance of unique_object_attr_accessor_type. static unique_object_attr_accessor_type access_unique_object_attribute( - const std::string &base_dir_path); + const path_type &base_path); /// \brief Returns an instance that provides access to the attribute of - /// anonymous object. \param base_dir_path Path to a data store. \return + /// anonymous object. \param base_path Path to a data store. \return /// Returns an instance of anonymous_object_attr_accessor_type. static anonymous_object_attr_accessor_type access_anonymous_object_attribute( - const std::string &base_dir_path); - - /// \brief Checks if this kernel is open. - /// \return Returns true if it is open; otherwise, returns false. - bool is_open() const noexcept; + const path_type &base_path); /// \brief Checks if the status of this kernel is good. /// \return Returns true if it is good; otherwise, returns false. @@ -496,37 +472,29 @@ class manager_kernel { // Private methods // -------------------- // - void priv_sanity_check() const; + void priv_check_sanity() const; bool priv_validate_runtime_configuration() const; - difference_type priv_to_offset(const void *const ptr) const; + difference_type priv_to_offset(const void *ptr) const; void *priv_to_address(difference_type offset) const; // ---------- For data store structure ---------- // // Directory structure: - // base_dir_path/ <- this path is given by user - // top_dir/ + // base_path/ <- this path is given by user + // top_dir/ <- managed by storage class // some top-level files // management_dir/ // directories and files for allocation management - // segment_dir/ + // segment_dir/ <- managed by segment_storage class // directories and files for application data segment - static std::string priv_make_top_dir_path(const std::string &base_dir_path); - static std::string priv_make_top_level_file_name( - const std::string &base_dir_path, const std::string &item_name); - static std::string priv_make_management_dir_path( - const std::string &base_dir_path); - static std::string priv_make_management_file_name( - const std::string &base_dir_path, const std::string &item_name); - static std::string priv_make_segment_dir_path( - const std::string &base_dir_path); - static bool priv_init_datastore_directory(const std::string &base_dir_path); + + static bool priv_create_datastore_directory(const path_type &base_path); // ---------- For consistence support ---------- // - static bool priv_consistent(const std::string &base_dir_path); + static bool priv_consistent(const path_type &base_path); static bool priv_check_version(const json_store &metadata_json); - static bool priv_properly_closed(const std::string &base_dir_path); - static bool priv_mark_properly_closed(const std::string &base_dir_path); - static bool priv_unmark_properly_closed(const std::string &base_dir_path); + static bool priv_properly_closed(const path_type &base_path); + static bool priv_mark_properly_closed(const path_type &base_path); + static bool priv_unmark_properly_closed(const path_type &base_path); // ---------- For constructed objects ---------- // template @@ -538,21 +506,15 @@ class manager_kernel { difference_type offset, size_type length); - bool priv_remove_attr_object_no_mutex(const difference_type offset); + bool priv_remove_attr_object_no_mutex(difference_type offset); template - void priv_destruct_and_free_memory(const difference_type offset, - const size_type length); + void priv_destruct_and_free_memory(difference_type offset, size_type length); // ---------- For segment ---------- // - bool priv_reserve_vm_region(size_type nbytes); - bool priv_release_vm_region(); - bool priv_allocate_segment_header(void *addr); - bool priv_deallocate_segment_header(); - - bool priv_open(const char *base_dir_path, bool read_only, + bool priv_open(const path_type &base_path, bool read_only, size_type vm_reserve_size_request = 0); - bool priv_create(const char *base_dir_path, size_type vm_reserve_size); + bool priv_create(const path_type &base_path, size_type vm_reserve_size); // ---------- For serializing/deserializing ---------- // bool priv_serialize_management_data(); @@ -560,23 +522,22 @@ class manager_kernel { // ---------- snapshot ---------- // /// \brief Takes a snapshot. The snapshot has a different UUID. - bool priv_snapshot(const char *destination_base_dir_path, const bool clone, - const int num_max_copy_threads); + bool priv_snapshot(const path_type &destination_base_path, bool clone, + int num_max_copy_threads); // ---------- File operations ---------- // /// \brief Copies all backing files using reflink if possible - static bool priv_copy_data_store(const std::string &src_base_dir_path, - const std::string &dst_base_dir_path, - const bool clone, - const int num_max_copy_threads); + static bool priv_copy_data_store(const path_type &src_base_path, + const path_type &dst_base_path, bool clone, + int num_max_copy_threads); /// \brief Removes all backing files - static bool priv_remove_data_store(const std::string &dir_path); + static bool priv_remove_data_store(const path_type &base_path); // ---------- Management metadata ---------- // - static bool priv_read_management_metadata(const std::string &base_dir_path, + static bool priv_read_management_metadata(const path_type &base_path, json_store *json_root); - static bool priv_write_management_metadata(const std::string &base_dir_path, + static bool priv_write_management_metadata(const path_type &base_path, const json_store &json_root); static version_type priv_get_version(const json_store &metadata_json); @@ -586,25 +547,22 @@ class manager_kernel { static std::string priv_get_uuid(const json_store &metadata_json); // ---------- Description ---------- // - static bool priv_read_description(const std::string &base_dir_path, + static bool priv_read_description(const path_type &base_path, std::string *description); - static bool priv_write_description(const std::string &base_dir_path, + static bool priv_write_description(const path_type &base_path, const std::string &description); // -------------------- // // Private fields // -------------------- // bool m_good{false}; - std::string m_base_dir_path{}; - size_type m_vm_region_size{0}; - void *m_vm_region{nullptr}; - segment_header_type *m_segment_header{nullptr}; + path_type m_base_path{}; attributed_object_directory_type m_named_object_directory{}; attributed_object_directory_type m_unique_object_directory{}; attributed_object_directory_type m_anonymous_object_directory{}; - segment_storage_type m_segment_storage{}; segment_memory_allocator m_segment_memory_allocator{nullptr}; std::unique_ptr m_manager_metadata{nullptr}; + segment_storage m_segment_storage{}; #ifdef METALL_ENABLE_MUTEX_IN_MANAGER_KERNEL std::unique_ptr m_object_directories_mutex{nullptr}; diff --git a/include/metall/kernel/manager_kernel_fwd.hpp b/include/metall/kernel/manager_kernel_fwd.hpp index 0a7ccc3e..e64d29e2 100644 --- a/include/metall/kernel/manager_kernel_fwd.hpp +++ b/include/metall/kernel/manager_kernel_fwd.hpp @@ -10,9 +10,12 @@ namespace metall { namespace kernel { /// \brief Manager kernel class version 0 +/// \tparam storage Storage manager. +/// \tparam segment_storage Segment storage manager. /// \tparam chunk_no_type Type of chunk number /// \tparam chunk_size Size of single chunk in byte -template +template class manager_kernel; } // namespace kernel diff --git a/include/metall/kernel/manager_kernel_impl.ipp b/include/metall/kernel/manager_kernel_impl.ipp index 2d9f7641..0ed49374 100644 --- a/include/metall/kernel/manager_kernel_impl.ipp +++ b/include/metall/kernel/manager_kernel_impl.ipp @@ -8,14 +8,13 @@ #include -namespace metall { -namespace kernel { +namespace metall::kernel { // -------------------- // // Constructor // -------------------- // -template -manager_kernel::manager_kernel() +template +manager_kernel::manager_kernel() : m_segment_memory_allocator(&m_segment_storage) { m_manager_metadata = std::make_unique(); if (!m_manager_metadata) { @@ -30,63 +29,61 @@ manager_kernel::manager_kernel() m_good = priv_validate_runtime_configuration(); } -template -manager_kernel::~manager_kernel() noexcept { +template +manager_kernel::~manager_kernel() noexcept { close(); } // -------------------- // // Public methods // -------------------- // -template -bool manager_kernel::create(const char *base_dir_path, - const size_type vm_reserve_size) { - return m_good = priv_create(base_dir_path, vm_reserve_size); +template +bool manager_kernel::create(const path_type &base_path, + const size_type vm_reserve_size) { + return m_good = priv_create(base_path, vm_reserve_size); } -template -bool manager_kernel::open_read_only( - const char *base_dir_path) { - return m_good = priv_open(base_dir_path, true, 0); +template +bool manager_kernel::open_read_only( + const path_type &base_path) { + return m_good = priv_open(base_path, true, 0); } -template -bool manager_kernel::open( - const char *base_dir_path, const size_type vm_reserve_size_request) { - return m_good = priv_open(base_dir_path, false, vm_reserve_size_request); +template +bool manager_kernel::open( + const path_type &base_path, const size_type vm_reserve_size_request) { + return m_good = priv_open(base_path, false, vm_reserve_size_request); } -template -void manager_kernel::close() { - if (m_vm_region) { - priv_sanity_check(); +template +void manager_kernel::close() { + if (m_segment_storage.is_open()) { + priv_check_sanity(); if (!m_segment_storage.read_only()) { priv_serialize_management_data(); m_segment_storage.sync(true); } m_good = false; - m_segment_storage.destroy(); - priv_deallocate_segment_header(); - priv_release_vm_region(); + m_segment_storage.release(); if (!m_segment_storage.read_only()) { // This function must be called at the end - priv_mark_properly_closed(m_base_dir_path); + priv_mark_properly_closed(m_base_path); } } } -template -void manager_kernel::flush(const bool synchronous) { - priv_sanity_check(); +template +void manager_kernel::flush(const bool synchronous) { + priv_check_sanity(); m_segment_storage.sync(synchronous); } -template -void *manager_kernel::allocate( - const manager_kernel::size_type nbytes) { - priv_sanity_check(); +template +void *manager_kernel::allocate( + const manager_kernel::size_type nbytes) { + priv_check_sanity(); if (m_segment_storage.read_only()) return nullptr; const auto offset = m_segment_memory_allocator.allocate(nbytes); @@ -98,11 +95,11 @@ void *manager_kernel::allocate( return priv_to_address(offset); } -template -void *manager_kernel::allocate_aligned( - const manager_kernel::size_type nbytes, - const manager_kernel::size_type alignment) { - priv_sanity_check(); +template +void *manager_kernel::allocate_aligned( + const manager_kernel::size_type nbytes, + const manager_kernel::size_type alignment) { + priv_check_sanity(); if (m_segment_storage.read_only()) return nullptr; // This requirement could be removed, but it would need some work to do @@ -120,25 +117,25 @@ void *manager_kernel::allocate_aligned( return addr; } -template -void manager_kernel::deallocate(void *addr) { - priv_sanity_check(); +template +void manager_kernel::deallocate(void *const addr) { + priv_check_sanity(); if (m_segment_storage.read_only()) return; if (!addr) return; m_segment_memory_allocator.deallocate(priv_to_offset(addr)); } -template -bool manager_kernel::all_memory_deallocated() const { - priv_sanity_check(); +template +bool manager_kernel::all_memory_deallocated() const { + priv_check_sanity(); return m_segment_memory_allocator.all_memory_deallocated(); } -template +template template -std::pair::size_type> -manager_kernel::find(char_ptr_holder_type name) const { - priv_sanity_check(); +std::pair::size_type> +manager_kernel::find(char_ptr_holder_type name) const { + priv_check_sanity(); if (name.is_anonymous()) { return std::make_pair(nullptr, 0); @@ -163,10 +160,10 @@ manager_kernel::find(char_ptr_holder_type name) const { return std::make_pair(nullptr, 0); } -template +template template -bool manager_kernel::destroy(char_ptr_holder_type name) { - priv_sanity_check(); +bool manager_kernel::destroy(char_ptr_holder_type name) { + priv_check_sanity(); if (m_segment_storage.read_only()) return false; if (name.is_anonymous()) { @@ -196,10 +193,10 @@ bool manager_kernel::destroy(char_ptr_holder_type name) { return true; } -template +template template -bool manager_kernel::destroy_ptr(const T *ptr) { - priv_sanity_check(); +bool manager_kernel::destroy_ptr(const T *ptr) { + priv_check_sanity(); if (m_segment_storage.read_only()) return false; size_type length = 0; @@ -223,10 +220,10 @@ bool manager_kernel::destroy_ptr(const T *ptr) { return true; } -template +template template -const typename manager_kernel::char_type * -manager_kernel::get_instance_name(const T *ptr) const { +const typename manager_kernel::char_type * +manager_kernel::get_instance_name(const T *ptr) const { auto nitr = m_named_object_directory.find(priv_to_offset(ptr)); if (nitr != m_named_object_directory.end()) { return nitr->name().c_str(); @@ -241,10 +238,10 @@ manager_kernel::get_instance_name(const T *ptr) const { // object } -template +template template -typename manager_kernel::instance_kind -manager_kernel::get_instance_kind(const T *ptr) const { +typename manager_kernel::instance_kind +manager_kernel::get_instance_kind(const T *ptr) const { if (m_named_object_directory.count(priv_to_offset(ptr)) > 0) { return instance_kind::named_kind; } @@ -261,10 +258,10 @@ manager_kernel::get_instance_kind(const T *ptr) const { return instance_kind(); } -template +template template -typename manager_kernel::size_type -manager_kernel::get_instance_length(const T *ptr) const { +typename manager_kernel::size_type +manager_kernel::get_instance_length(const T *ptr) const { { auto itr = m_named_object_directory.find(priv_to_offset(ptr)); if (itr != m_named_object_directory.end()) { @@ -292,9 +289,9 @@ manager_kernel::get_instance_length(const T *ptr) const { return 0; // Won't treat as an error } -template +template template -bool manager_kernel::is_instance_type( +bool manager_kernel::is_instance_type( const void *const ptr) const { { auto itr = m_named_object_directory.find(priv_to_offset(ptr)); @@ -320,9 +317,9 @@ bool manager_kernel::is_instance_type( return false; } -template +template template -bool manager_kernel::get_instance_description( +bool manager_kernel::get_instance_description( const T *ptr, std::string *description) const { { auto itr = m_named_object_directory.find(priv_to_offset(ptr)); @@ -351,9 +348,9 @@ bool manager_kernel::get_instance_description( return false; } -template +template template -bool manager_kernel::set_instance_description( +bool manager_kernel::set_instance_description( const T *ptr, const std::string &description) { if (m_segment_storage.read_only()) return false; @@ -366,154 +363,155 @@ bool manager_kernel::set_instance_description( m_anonymous_object_directory.find(priv_to_offset(ptr)), description)); } -template -typename manager_kernel::size_type -manager_kernel::get_num_named_objects() const { +template +typename manager_kernel::size_type +manager_kernel::get_num_named_objects() const { return m_named_object_directory.size(); } -template -typename manager_kernel::size_type -manager_kernel::get_num_unique_objects() const { +template +typename manager_kernel::size_type +manager_kernel::get_num_unique_objects() const { return m_unique_object_directory.size(); } -template -typename manager_kernel::size_type -manager_kernel::get_num_anonymous_objects() const { +template +typename manager_kernel::size_type +manager_kernel::get_num_anonymous_objects() const { return m_anonymous_object_directory.size(); } -template -typename manager_kernel::const_named_iterator -manager_kernel::named_begin() const { +template +typename manager_kernel::const_named_iterator +manager_kernel::named_begin() const { return m_named_object_directory.begin(); } -template -typename manager_kernel::const_named_iterator -manager_kernel::named_end() const { +template +typename manager_kernel::const_named_iterator +manager_kernel::named_end() const { return m_named_object_directory.end(); } -template -typename manager_kernel::const_unique_iterator -manager_kernel::unique_begin() const { +template +typename manager_kernel::const_unique_iterator +manager_kernel::unique_begin() const { return m_unique_object_directory.begin(); } -template -typename manager_kernel::const_unique_iterator -manager_kernel::unique_end() const { +template +typename manager_kernel::const_unique_iterator +manager_kernel::unique_end() const { return m_unique_object_directory.end(); } -template -typename manager_kernel::const_anonymous_iterator -manager_kernel::anonymous_begin() const { +template +typename manager_kernel::const_anonymous_iterator +manager_kernel::anonymous_begin() const { return m_anonymous_object_directory.begin(); } -template -typename manager_kernel::const_anonymous_iterator -manager_kernel::anonymous_end() const { +template +typename manager_kernel::const_anonymous_iterator +manager_kernel::anonymous_end() const { return m_anonymous_object_directory.end(); } -template +template template -T *manager_kernel::generic_construct( +T *manager_kernel::generic_construct( char_ptr_holder_type name, const size_type num, const bool try2find, [[maybe_unused]] const bool do_throw, mdtl::in_place_interface &table) { - priv_sanity_check(); + priv_check_sanity(); return priv_generic_construct(name, num, try2find, table); } -template -const typename manager_kernel::segment_header_type * -manager_kernel::get_segment_header() const { - return reinterpret_cast(m_segment_header); +template +const typename manager_kernel::segment_header_type & +manager_kernel::get_segment_header() const { + return m_segment_storage.get_segment_header(); } -template -const void *manager_kernel::get_segment() const { +template +const void *manager_kernel::get_segment() const { return m_segment_storage.get_segment(); } -template -typename manager_kernel::size_type -manager_kernel::get_segment_size() const { +template +typename manager_kernel::size_type +manager_kernel::get_segment_size() const { return m_segment_storage.size(); } -template -bool manager_kernel::snapshot( - const char *destination_base_dir_path, const bool clone, +template +bool manager_kernel::snapshot( + const path_type &destination_base_path, const bool clone, const int num_max_copy_threads) { - return priv_snapshot(destination_base_dir_path, clone, num_max_copy_threads); + return priv_snapshot(destination_base_path, clone, num_max_copy_threads); } -template -bool manager_kernel::copy( - const char *source_base_dir_path, const char *destination_base_dir_path, +template +bool manager_kernel::copy( + const path_type &source_base_path, const path_type &destination_base_path, const bool clone, const int num_max_copy_threads) { - return priv_copy_data_store(source_base_dir_path, destination_base_dir_path, - clone, num_max_copy_threads); + return priv_copy_data_store(source_base_path, destination_base_path, clone, + num_max_copy_threads); } -template -std::future manager_kernel::copy_async( - const char *source_dir_path, const char *destination_dir_path, +template +std::future manager_kernel::copy_async( + const path_type &source_base_path, const path_type &destination_base_path, const bool clone, const int num_max_copy_threads) { - return std::async(std::launch::async, copy, source_dir_path, - destination_dir_path, clone, num_max_copy_threads); + return std::async(std::launch::async, copy, source_base_path, + destination_base_path, clone, num_max_copy_threads); } -template -bool manager_kernel::remove(const char *base_dir_path) { - return priv_remove_data_store(base_dir_path); +template +bool manager_kernel::remove(const path_type &base_path) { + return priv_remove_data_store(base_path); } -template -std::future manager_kernel::remove_async( - const char *base_dir_path) { - return std::async(std::launch::async, remove, base_dir_path); +template +std::future manager_kernel::remove_async( + const path_type &base_path) { + return std::async(std::launch::async, remove, base_path); } -template -bool manager_kernel::consistent(const char *dir_path) { - return priv_consistent(dir_path); +template +bool manager_kernel::consistent(const path_type &base_path) { + return priv_consistent(base_path); } -template -std::string manager_kernel::get_uuid() const { - return self_type::get_uuid(m_base_dir_path); +template +std::string manager_kernel::get_uuid() const { + return self_type::get_uuid(m_base_path); } -template -std::string manager_kernel::get_uuid(const char *dir_path) { +template +std::string manager_kernel::get_uuid( + const path_type &base_path) { json_store meta_data; - if (!priv_read_management_metadata(dir_path, &meta_data)) { + if (!priv_read_management_metadata(base_path, &meta_data)) { std::stringstream ss; - ss << "Cannot read management metadata in " << dir_path; + ss << "Cannot read management metadata in " << base_path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return ""; } return priv_get_uuid(meta_data); } -template -version_type manager_kernel::get_version() const { - return self_type::get_version(m_base_dir_path); +template +version_type manager_kernel::get_version() const { + return self_type::get_version(m_base_path); } -template -version_type manager_kernel::get_version( - const char *dir_path) { +template +version_type manager_kernel::get_version( + const path_type &base_path) { json_store meta_data; - if (!priv_read_management_metadata(dir_path, &meta_data)) { + if (!priv_read_management_metadata(base_path, &meta_data)) { std::stringstream ss; - ss << "Cannot read management metadata in " << dir_path; + ss << "Cannot read management metadata in " << base_path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return 0; } @@ -521,135 +519,89 @@ version_type manager_kernel::get_version( return (version == ver_detail::k_error_version) ? 0 : version; } -template -bool manager_kernel::get_description( - const std::string &base_dir_path, std::string *description) { - return priv_read_description(base_dir_path, description); +template +bool manager_kernel::get_description( + const path_type &base_path, std::string *description) { + return priv_read_description(base_path, description); } -template -bool manager_kernel::get_description( +template +bool manager_kernel::get_description( std::string *description) const { - return priv_read_description(m_base_dir_path, description); + return priv_read_description(m_base_path, description); } -template -bool manager_kernel::set_description( - const std::string &base_dir_path, const std::string &description) { - return priv_write_description(base_dir_path, description); +template +bool manager_kernel::set_description( + const path_type &base_path, const std::string &description) { + return priv_write_description(base_path, description); } -template -bool manager_kernel::set_description( +template +bool manager_kernel::set_description( const std::string &description) { - return set_description(m_base_dir_path, description); + return set_description(m_base_path, description); } -template -typename manager_kernel::named_object_attr_accessor_type -manager_kernel::access_named_object_attribute( - const std::string &base_dir_path) { - return named_object_attr_accessor_type(priv_make_management_file_name( - base_dir_path, k_named_object_directory_prefix)); +template +typename manager_kernel::named_object_attr_accessor_type +manager_kernel::access_named_object_attribute( + const path_type &base_path) { + return named_object_attr_accessor_type(storage::get_path( + base_path, {k_management_dir_name, k_named_object_directory_prefix})); } -template -typename manager_kernel::unique_object_attr_accessor_type -manager_kernel::access_unique_object_attribute( - const std::string &base_dir_path) { - return unique_object_attr_accessor_type(priv_make_management_file_name( - base_dir_path, k_unique_object_directory_prefix)); +template +typename manager_kernel::unique_object_attr_accessor_type +manager_kernel::access_unique_object_attribute( + const path_type &base_path) { + return unique_object_attr_accessor_type(storage::get_path( + base_path, {k_management_dir_name, k_unique_object_directory_prefix})); } -template -typename manager_kernel::anonymous_object_attr_accessor_type -manager_kernel::access_anonymous_object_attribute( - const std::string &base_dir_path) { - return anonymous_object_attr_accessor_type(priv_make_management_file_name( - base_dir_path, k_anonymous_object_directory_prefix)); +template +typename manager_kernel::anonymous_object_attr_accessor_type +manager_kernel::access_anonymous_object_attribute( + const path_type &base_path) { + return anonymous_object_attr_accessor_type(storage::get_path( + base_path, {k_management_dir_name, k_anonymous_object_directory_prefix})); } -template -bool manager_kernel::good() const noexcept { +template +bool manager_kernel::good() const noexcept { return m_good; } // -------------------- // // Private methods // -------------------- // -template -typename manager_kernel::difference_type -manager_kernel::priv_to_offset(const void *const ptr) const { +template +typename manager_kernel::difference_type +manager_kernel::priv_to_offset(const void *const ptr) const { return static_cast(const_cast(ptr)) - static_cast(m_segment_storage.get_segment()); } -template -void *manager_kernel::priv_to_address( +template +void *manager_kernel::priv_to_address( const difference_type offset) const { return static_cast(m_segment_storage.get_segment()) + offset; } -template -std::string manager_kernel::priv_make_top_dir_path( - const std::string &base_dir_path) { - return base_dir_path + "/" + k_datastore_top_dir_name; -} - -template -std::string manager_kernel::priv_make_top_level_file_name( - const std::string &base_dir_path, const std::string &item_name) { - return priv_make_top_dir_path(base_dir_path) + "/" + item_name; -} - -template -std::string manager_kernel::priv_make_management_dir_path( - const std::string &base_dir_path) { - return priv_make_top_dir_path(base_dir_path) + "/" + - k_datastore_management_dir_name + "/"; -} - -template -std::string manager_kernel::priv_make_management_file_name( - const std::string &base_dir_path, const std::string &item_name) { - return priv_make_management_dir_path(base_dir_path) + "/" + item_name; -} - -template -std::string manager_kernel::priv_make_segment_dir_path( - const std::string &base_dir_path) { - return priv_make_top_dir_path(base_dir_path) + "/" + - k_datastore_segment_dir_name + "/"; -} - -template -bool manager_kernel::priv_init_datastore_directory( - const std::string &base_dir_path) { - // Create the base directory if needed - if (!mdtl::create_directory(base_dir_path)) { - std::string s("Failed to create directory: " + base_dir_path); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - return false; - } - - // Remove existing directory to certainly create a new data store - if (!remove(base_dir_path.c_str())) { - std::string s("Failed to remove a directory: " + base_dir_path); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); +template +bool manager_kernel::priv_create_datastore_directory( + const path_type &base_path) { + if (!storage::create(base_path)) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to initialize the data store directory"); return false; } // Create internal directories if needed - if (!mdtl::create_directory(priv_make_management_dir_path(base_dir_path))) { + if (!mdtl::create_directory( + storage::get_path(base_path, k_management_dir_name))) { std::string s("Failed to create directory: " + - priv_make_management_dir_path(base_dir_path)); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - return false; - } - - if (!mdtl::create_directory(priv_make_segment_dir_path(base_dir_path))) { - std::string s("Failed to create directory: " + - priv_make_segment_dir_path(base_dir_path)); + storage::get_path(base_path, k_management_dir_name).string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -657,20 +609,17 @@ bool manager_kernel::priv_init_datastore_directory( return true; } -template -void manager_kernel::priv_sanity_check() const { +template +void manager_kernel::priv_check_sanity() const { assert(m_good); - assert(!m_base_dir_path.empty()); - assert(m_vm_region_size > 0); - assert(m_vm_region); - assert(m_segment_header); + assert(!m_base_path.empty()); + assert(m_segment_storage.check_sanity()); // TODO: add sanity check functions in other classes - assert(m_segment_storage.get_segment()); assert(m_manager_metadata); } -template -bool manager_kernel::priv_validate_runtime_configuration() +template +bool manager_kernel::priv_validate_runtime_configuration() const { const auto system_page_size = mdtl::get_page_size(); if (system_page_size <= 0) { @@ -702,115 +651,45 @@ bool manager_kernel::priv_validate_runtime_configuration() return true; } -template -bool manager_kernel::priv_consistent( - const std::string &base_dir_path) { +template +bool manager_kernel::priv_consistent( + const path_type &base_path) { json_store metadata; - return priv_properly_closed(base_dir_path) && - (priv_read_management_metadata(base_dir_path, &metadata) && + return priv_properly_closed(base_path) && + (priv_read_management_metadata(base_path, &metadata) && priv_check_version(metadata)); } -template -bool manager_kernel::priv_check_version( +template +bool manager_kernel::priv_check_version( const json_store &metadata_json) { return priv_get_version(metadata_json) == version_type(METALL_VERSION); } -template -bool manager_kernel::priv_properly_closed( - const std::string &base_dir_path) { - return mdtl::file_exist(priv_make_top_level_file_name( - base_dir_path, k_properly_closed_mark_file_name)); -} - -template -bool manager_kernel::priv_mark_properly_closed( - const std::string &base_dir_path) { - return mdtl::create_file(priv_make_top_level_file_name( - base_dir_path, k_properly_closed_mark_file_name)); +template +bool manager_kernel::priv_properly_closed( + const path_type &base_path) { + return mdtl::file_exist( + storage::get_path(base_path, k_properly_closed_mark_file_name)); } -template -bool manager_kernel::priv_unmark_properly_closed( - const std::string &base_dir_path) { - return mdtl::remove_file(priv_make_top_level_file_name( - base_dir_path, k_properly_closed_mark_file_name)); +template +bool manager_kernel::priv_mark_properly_closed( + const path_type &base_path) { + return mdtl::create_file( + storage::get_path(base_path, k_properly_closed_mark_file_name)); } -template -bool manager_kernel::priv_reserve_vm_region( - const size_type nbytes) { - // Align the VM region to the page size to decrease the implementation cost of - // some features, such as supporting Umap and aligned allocation - const auto alignment = k_chunk_size; - - assert(alignment > 0); - m_vm_region_size = mdtl::round_up(nbytes, alignment); - m_vm_region = mdtl::reserve_aligned_vm_region(alignment, m_vm_region_size); - if (!m_vm_region) { - std::stringstream ss; - ss << "Cannot reserve a VM region " << nbytes << " bytes"; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - m_vm_region_size = 0; - return false; - } - assert(reinterpret_cast(m_vm_region) % alignment == 0); - - return true; -} - -template -bool manager_kernel::priv_release_vm_region() { - if (!mdtl::munmap(m_vm_region, m_vm_region_size, false)) { - std::stringstream ss; - ss << "Cannot release a VM region " << (uint64_t)m_vm_region << ", " - << m_vm_region_size << " bytes."; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - m_vm_region = nullptr; - m_vm_region_size = 0; - - return true; +template +bool manager_kernel::priv_unmark_properly_closed( + const path_type &base_path) { + return mdtl::remove_file( + storage::get_path(base_path, k_properly_closed_mark_file_name)); } -template -bool manager_kernel::priv_allocate_segment_header( - void *const addr) { - if (!addr) { - return false; - } - - if (mdtl::map_anonymous_write_mode(addr, k_segment_header_size, MAP_FIXED) != - addr) { - logger::out(logger::level::error, __FILE__, __LINE__, - "Cannot allocate segment header"); - return false; - } - m_segment_header = reinterpret_cast(addr); - - new (m_segment_header) segment_header_type(); - m_segment_header->manager_kernel_address = this; - - return true; -} - -template -bool manager_kernel::priv_deallocate_segment_header() { - std::destroy_at(&m_segment_header); - const auto ret = mdtl::munmap(m_segment_header, k_segment_header_size, false); - m_segment_header = nullptr; - if (!ret) { - logger::out(logger::level::error, __FILE__, __LINE__, - "Failed to deallocate segment header"); - } - return ret; -} - -template +template template -T *manager_kernel::priv_generic_construct( +T *manager_kernel::priv_generic_construct( char_ptr_holder_type name, size_type length, bool try2find, mdtl::in_place_interface &table) { // Check overflow for security @@ -879,9 +758,9 @@ T *manager_kernel::priv_generic_construct( return static_cast(ptr); } -template +template template -bool manager_kernel::priv_register_attr_object_no_mutex( +bool manager_kernel::priv_register_attr_object_no_mutex( char_ptr_holder_type name, difference_type offset, size_type length) { if (name.is_anonymous()) { if (!m_anonymous_object_directory.insert("", offset, length, @@ -915,8 +794,8 @@ bool manager_kernel::priv_register_attr_object_no_mutex( return true; } -template -bool manager_kernel::priv_remove_attr_object_no_mutex( +template +bool manager_kernel::priv_remove_attr_object_no_mutex( difference_type offset) { // As the instance kind of the object is not given, // just call the eranse functions in all tables to simplify implementation. @@ -931,9 +810,9 @@ bool manager_kernel::priv_remove_attr_object_no_mutex( return true; } -template +template template -void manager_kernel::priv_destruct_and_free_memory( +void manager_kernel::priv_destruct_and_free_memory( const difference_type offset, const size_type length) { auto *object = static_cast(priv_to_address(offset)); // Destruct each object, can throw @@ -942,15 +821,15 @@ void manager_kernel::priv_destruct_and_free_memory( m_segment_memory_allocator.deallocate(offset); } -template -bool manager_kernel::priv_open( - const char *base_dir_path, const bool read_only, +template +bool manager_kernel::priv_open( + const path_type &base_path, const bool read_only, const size_type vm_reserve_size_request) { if (!priv_validate_runtime_configuration()) { return false; } - if (!priv_read_management_metadata(base_dir_path, m_manager_metadata.get())) { + if (!priv_read_management_metadata(base_path, m_manager_metadata.get())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to read management metadata"); return false; @@ -965,63 +844,41 @@ bool manager_kernel::priv_open( return false; } - if (!priv_properly_closed(base_dir_path)) { + if (!priv_properly_closed(base_path)) { logger::out(logger::level::error, __FILE__, __LINE__, "Inconsistent data store — it was not closed properly and " "might have been collapsed."); return false; } - m_base_dir_path = base_dir_path; - - const size_type existing_segment_size = segment_storage_type::get_size( - priv_make_segment_dir_path(m_base_dir_path)); - const size_type vm_reserve_size = - (read_only) ? existing_segment_size + k_segment_header_size - : std::max(existing_segment_size + k_segment_header_size, - vm_reserve_size_request); - - if (!priv_reserve_vm_region(vm_reserve_size)) { - return false; - } - - if (!priv_allocate_segment_header(m_vm_region)) { - priv_release_vm_region(); - return false; - } + m_base_path = base_path; // Clear the consistent mark before opening with the write mode - if (!read_only && !priv_unmark_properly_closed(m_base_dir_path)) { + if (!read_only && !priv_unmark_properly_closed(m_base_path)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to erase the properly close mark before opening"); - priv_deallocate_segment_header(); - priv_release_vm_region(); return false; } - if (!m_segment_storage.open( - priv_make_segment_dir_path(m_base_dir_path), - m_vm_region_size - k_segment_header_size, - static_cast(m_vm_region) + k_segment_header_size, - read_only)) { - priv_deallocate_segment_header(); - priv_release_vm_region(); + if (!m_segment_storage.open(m_base_path, vm_reserve_size_request, + read_only)) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to open the application data segment"); return false; } + m_segment_storage.get_segment_header().manager_kernel_address = this; if (!priv_deserialize_management_data()) { - m_segment_storage.destroy(); - priv_deallocate_segment_header(); - priv_release_vm_region(); + m_segment_storage.release(); return false; } return true; } -template -bool manager_kernel::priv_create( - const char *base_dir_path, const size_type vm_reserve_size) { +template +bool manager_kernel::priv_create( + const path_type &base_path, const size_type vm_reserve_size) { if (!priv_validate_runtime_configuration()) { return false; } @@ -1034,43 +891,33 @@ bool manager_kernel::priv_create( return false; } - m_base_dir_path = base_dir_path; - - if (!priv_unmark_properly_closed(m_base_dir_path) || - !priv_init_datastore_directory(base_dir_path)) { + if (!priv_create_datastore_directory(base_path)) { std::stringstream ss; - ss << "Failed to initialize datastore directory under " << base_dir_path; + ss << "Failed to initialize datastore under " << base_path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } - if (!priv_reserve_vm_region(vm_reserve_size)) { + if (!priv_unmark_properly_closed(base_path)) { + std::stringstream ss; + ss << "Failed to remove a closed mark under " << base_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } - if (!priv_allocate_segment_header(m_vm_region)) { - priv_release_vm_region(); - return false; - } + m_base_path = base_path; - if (!m_segment_storage.create( - priv_make_segment_dir_path(m_base_dir_path), - m_vm_region_size - k_segment_header_size, - static_cast(m_vm_region) + k_segment_header_size, - k_initial_segment_size)) { + if (!m_segment_storage.create(m_base_path, vm_reserve_size)) { logger::out(logger::level::error, __FILE__, __LINE__, - "Cannot create application data segment"); - priv_deallocate_segment_header(); - priv_release_vm_region(); + "Cannot create an application data segment"); return false; } + m_segment_storage.get_segment_header().manager_kernel_address = this; if (!priv_set_uuid(m_manager_metadata.get()) || !priv_set_version(m_manager_metadata.get()) || - !priv_write_management_metadata(m_base_dir_path, *m_manager_metadata)) { - m_segment_storage.destroy(); - priv_deallocate_segment_header(); - priv_release_vm_region(); + !priv_write_management_metadata(m_base_path, *m_manager_metadata)) { + m_segment_storage.release(); return false; } @@ -1078,15 +925,15 @@ bool manager_kernel::priv_create( } // ---------- For serializing/deserializing ---------- // -template -bool manager_kernel::priv_serialize_management_data() { - priv_sanity_check(); +template +bool manager_kernel::priv_serialize_management_data() { + priv_check_sanity(); if (m_segment_storage.read_only()) return true; if (!m_named_object_directory.serialize( - priv_make_management_file_name(m_base_dir_path, - k_named_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_named_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to serialize named object directory"); @@ -1094,8 +941,8 @@ bool manager_kernel::priv_serialize_management_data() { } if (!m_unique_object_directory.serialize( - priv_make_management_file_name(m_base_dir_path, - k_unique_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_unique_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to serialize unique object directory"); @@ -1103,27 +950,28 @@ bool manager_kernel::priv_serialize_management_data() { } if (!m_anonymous_object_directory.serialize( - priv_make_management_file_name(m_base_dir_path, - k_anonymous_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_anonymous_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to serialize anonymous object directory"); return false; } - if (!m_segment_memory_allocator.serialize(priv_make_management_file_name( - m_base_dir_path, k_segment_memory_allocator_prefix))) { + if (!m_segment_memory_allocator.serialize(storage::get_path( + m_base_path, + {k_management_dir_name, k_segment_memory_allocator_prefix}))) { return false; } return true; } -template -bool manager_kernel::priv_deserialize_management_data() { +template +bool manager_kernel::priv_deserialize_management_data() { if (!m_named_object_directory.deserialize( - priv_make_management_file_name(m_base_dir_path, - k_named_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_named_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to deserialize named object directory"); @@ -1131,8 +979,8 @@ bool manager_kernel::priv_deserialize_management_data() { } if (!m_unique_object_directory.deserialize( - priv_make_management_file_name(m_base_dir_path, - k_unique_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_unique_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to deserialize unique object directory"); @@ -1140,16 +988,17 @@ bool manager_kernel::priv_deserialize_management_data() { } if (!m_anonymous_object_directory.deserialize( - priv_make_management_file_name(m_base_dir_path, - k_anonymous_object_directory_prefix) + storage::get_path(m_base_path, {k_management_dir_name, + k_anonymous_object_directory_prefix}) .c_str())) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to deserialize anonymous object directory"); return false; } - if (!m_segment_memory_allocator.deserialize(priv_make_management_file_name( - m_base_dir_path, k_segment_memory_allocator_prefix))) { + if (!m_segment_memory_allocator.deserialize(storage::get_path( + m_base_path, + {k_management_dir_name, k_segment_memory_allocator_prefix}))) { return false; } @@ -1157,44 +1006,34 @@ bool manager_kernel::priv_deserialize_management_data() { } // ---------- snapshot ---------- // -template -bool manager_kernel::priv_snapshot( - const char *destination_base_dir_path, const bool clone, +template +bool manager_kernel::priv_snapshot( + const path_type &destination_base_path, const bool clone, const int num_max_copy_threads) { - priv_sanity_check(); - m_segment_storage.sync(true); + priv_check_sanity(); priv_serialize_management_data(); - const auto dst_top_dir = priv_make_top_dir_path(destination_base_dir_path); - if (!mdtl::create_directory(dst_top_dir)) { + if (!priv_create_datastore_directory(destination_base_path)) { std::stringstream ss; - ss << "Failed to create a directory: " << dst_top_dir; + ss << "Failed to init the destination: " << destination_base_path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } // Copy segment directory - const auto src_seg_dir = priv_make_segment_dir_path(m_base_dir_path); - const auto dst_seg_dir = - priv_make_segment_dir_path(destination_base_dir_path); - if (!mdtl::create_directory(dst_seg_dir)) { - std::stringstream ss; - ss << "Failed to create directory: " << dst_seg_dir; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } - if (!m_segment_storage.copy(src_seg_dir, dst_seg_dir, clone, - num_max_copy_threads)) { + if (!m_segment_storage.snapshot(destination_base_path, clone, + num_max_copy_threads)) { std::stringstream ss; - ss << "Failed to copy " << src_seg_dir << " to " << dst_seg_dir; + ss << "Failed to copy " << m_base_path << " to " << destination_base_path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } - // Copy management dircotry - const auto src_mng_dir = priv_make_management_dir_path(m_base_dir_path); + // Copy management directory + const auto src_mng_dir = + storage::get_path(m_base_path, k_management_dir_name); const auto dst_mng_dir = - priv_make_management_dir_path(destination_base_dir_path); + storage::get_path(destination_base_path, k_management_dir_name); if (!mdtl::create_directory(dst_mng_dir)) { std::stringstream ss; ss << "Failed to create directory: " << dst_mng_dir; @@ -1215,11 +1054,11 @@ bool manager_kernel::priv_snapshot( json_store meta_data; if (!priv_set_uuid(&meta_data)) return false; if (!priv_set_version(&meta_data)) return false; - if (!priv_write_management_metadata(destination_base_dir_path, meta_data)) + if (!priv_write_management_metadata(destination_base_path, meta_data)) return false; // Finally, mark it as properly-closed - if (!priv_mark_properly_closed(destination_base_dir_path)) { + if (!priv_mark_properly_closed(destination_base_path)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create a properly closed mark"); return false; @@ -1229,53 +1068,34 @@ bool manager_kernel::priv_snapshot( } // ---------- File operations ---------- // -template -bool manager_kernel::priv_copy_data_store( - const std::string &src_base_dir_path, const std::string &dst_base_dir_path, +template +bool manager_kernel::priv_copy_data_store( + const path_type &src_base_path, const path_type &dst_base_path, const bool use_clone, const int num_max_copy_threads) { - if (!consistent(src_base_dir_path.data())) { - std::string s( - "Source directory is not consistnt (may not have closed properly or " - "may still be open): " + - src_base_dir_path); + if (!consistent(src_base_path)) { + std::string s("Source directory is not consistent: " + + src_base_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } - const std::string src_top_dir = priv_make_top_dir_path(src_base_dir_path); - if (!mdtl::directory_exist(src_top_dir)) { - std::string s("Source directory does not exist: " + src_top_dir); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - return false; - } - - if (!mdtl::create_directory(priv_make_top_dir_path(dst_base_dir_path))) { - std::string s("Failed to create directory: " + dst_base_dir_path); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); + if (!storage::create(dst_base_path)) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to initialize the datastore directory"); return false; } // Copy segment directory - const auto src_seg_dir = priv_make_segment_dir_path(src_base_dir_path); - const auto dst_seg_dir = priv_make_segment_dir_path(dst_base_dir_path); - if (!mdtl::create_directory(dst_seg_dir)) { - std::string s("Failed to create directory: " + dst_seg_dir); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - return false; - } - if (!segment_storage_type::copy(src_seg_dir, dst_seg_dir, use_clone, - num_max_copy_threads)) { - std::stringstream ss; - ss << "Failed to copy " << src_seg_dir << " to " << dst_seg_dir; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } + segment_storage::copy(src_base_path, dst_base_path, use_clone, + num_max_copy_threads); - // Copy management dircotry - const auto src_mng_dir = priv_make_management_dir_path(src_base_dir_path); - const auto dst_mng_dir = priv_make_management_dir_path(dst_base_dir_path); + // Copy management directory + const auto src_mng_dir = + storage::get_path(src_base_path, k_management_dir_name); + const auto dst_mng_dir = + storage::get_path(dst_base_path, k_management_dir_name); if (!mdtl::create_directory(dst_mng_dir)) { - std::string s("Failed to create directory: " + dst_mng_dir); + std::string s("Failed to create directory: " + dst_mng_dir.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -1290,7 +1110,7 @@ bool manager_kernel::priv_copy_data_store( } // Finally, mark it as properly-closed - if (!priv_mark_properly_closed(dst_base_dir_path)) { + if (!priv_mark_properly_closed(dst_base_path)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create a properly closed mark"); return false; @@ -1299,19 +1119,20 @@ bool manager_kernel::priv_copy_data_store( return true; } -template -bool manager_kernel::priv_remove_data_store( - const std::string &base_dir_path) { - return mdtl::remove_file(priv_make_top_dir_path(base_dir_path)); +template +bool manager_kernel::priv_remove_data_store( + const path_type &base_path) { + return storage::remove(base_path); } // ---------- Management metadata ---------- // -template -bool manager_kernel::priv_write_management_metadata( - const std::string &base_dir_path, const json_store &json_root) { +template +bool manager_kernel::priv_write_management_metadata( + const path_type &base_path, const json_store &json_root) { if (!mdtl::ptree::write_json( - json_root, priv_make_management_file_name( - base_dir_path, k_manager_metadata_file_name))) { + json_root, + storage::get_path(base_path, {k_management_dir_name, + k_manager_metadata_file_name}))) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to write management metadata"); return false; @@ -1320,12 +1141,13 @@ bool manager_kernel::priv_write_management_metadata( return true; } -template -bool manager_kernel::priv_read_management_metadata( - const std::string &base_dir_path, json_store *json_root) { - if (!mdtl::ptree::read_json(priv_make_management_file_name( - base_dir_path, k_manager_metadata_file_name), - json_root)) { +template +bool manager_kernel::priv_read_management_metadata( + const path_type &base_path, json_store *json_root) { + if (!mdtl::ptree::read_json( + storage::get_path( + base_path, {k_management_dir_name, k_manager_metadata_file_name}), + json_root)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to read management metadata"); return false; @@ -1333,8 +1155,8 @@ bool manager_kernel::priv_read_management_metadata( return true; } -template -version_type manager_kernel::priv_get_version( +template +version_type manager_kernel::priv_get_version( const json_store &metadata_json) { version_type version; if (!mdtl::ptree::get_value(metadata_json, k_manager_metadata_key_for_version, @@ -1344,8 +1166,8 @@ version_type manager_kernel::priv_get_version( return version; } -template -bool manager_kernel::priv_set_version( +template +bool manager_kernel::priv_set_version( json_store *metadata_json) { if (mdtl::ptree::count(*metadata_json, k_manager_metadata_key_for_version) > 0) { @@ -1362,8 +1184,8 @@ bool manager_kernel::priv_set_version( return true; } -template -std::string manager_kernel::priv_get_uuid( +template +std::string manager_kernel::priv_get_uuid( const json_store &metadata_json) { std::string uuid_string; if (!mdtl::ptree::get_value(metadata_json, k_manager_metadata_key_for_uuid, @@ -1374,9 +1196,8 @@ std::string manager_kernel::priv_get_uuid( return uuid_string; } -template -bool manager_kernel::priv_set_uuid( - json_store *metadata_json) { +template +bool manager_kernel::priv_set_uuid(json_store *metadata_json) { std::stringstream uuid_ss; uuid_ss << mdtl::uuid(mdtl::uuid_random_generator{}()); if (!uuid_ss) { @@ -1400,11 +1221,11 @@ bool manager_kernel::priv_set_uuid( // ---------- Description ---------- // -template -bool manager_kernel::priv_read_description( - const std::string &base_dir_path, std::string *description) { - const auto &file_name = - priv_make_management_file_name(base_dir_path, k_description_file_name); +template +bool manager_kernel::priv_read_description( + const path_type &base_path, std::string *description) { + const auto &file_name = storage::get_path( + base_path, {k_management_dir_name, k_description_file_name}); if (!mdtl::file_exist(file_name)) { return false; // This is not an error @@ -1412,7 +1233,7 @@ bool manager_kernel::priv_read_description( std::ifstream ifs(file_name); if (!ifs.is_open()) { - std::string s("Failed to open: " + file_name); + std::string s("Failed to open: " + file_name.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -1425,21 +1246,21 @@ bool manager_kernel::priv_read_description( return true; } -template -bool manager_kernel::priv_write_description( - const std::string &base_dir_path, const std::string &description) { - const auto &file_name = - priv_make_management_file_name(base_dir_path, k_description_file_name); +template +bool manager_kernel::priv_write_description( + const path_type &base_path, const std::string &description) { + const auto &file_name = storage::get_path( + base_path, {k_management_dir_name, k_description_file_name}); std::ofstream ofs(file_name); if (!ofs.is_open()) { - std::string s("Failed to open: " + file_name); + std::string s("Failed to open: " + file_name.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } if (!(ofs << description)) { - std::string s("Failed to write data:" + file_name); + std::string s("Failed to write data:" + file_name.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -1449,7 +1270,6 @@ bool manager_kernel::priv_write_description( return true; } -} // namespace kernel -} // namespace metall +} // namespace metall::kernel #endif // METALL_DETAIL_KERNEL_MANAGER_KERNEL_IMPL_IPP diff --git a/include/metall/kernel/manager_kernel_profile_impl.ipp b/include/metall/kernel/manager_kernel_profile_impl.ipp index 7475f9c7..06345fce 100644 --- a/include/metall/kernel/manager_kernel_profile_impl.ipp +++ b/include/metall/kernel/manager_kernel_profile_impl.ipp @@ -11,10 +11,9 @@ namespace metall { namespace kernel { -template +template template -void manager_kernel::profile( - out_stream_type *log_out) { +void manager_kernel::profile(out_stream_type *log_out) { m_segment_memory_allocator.profile(log_out); } diff --git a/include/metall/kernel/segment_header.hpp b/include/metall/kernel/segment_header.hpp index 77cb6096..794087c2 100644 --- a/include/metall/kernel/segment_header.hpp +++ b/include/metall/kernel/segment_header.hpp @@ -9,8 +9,9 @@ namespace metall::kernel { struct segment_header { - void *manager_kernel_address; + void *manager_kernel_address{nullptr}; + segment_header() = default; ~segment_header() noexcept { manager_kernel_address = nullptr; } }; diff --git a/include/metall/kernel/segment_storage/mmap_segment_storage.hpp b/include/metall/kernel/segment_storage.hpp similarity index 59% rename from include/metall/kernel/segment_storage/mmap_segment_storage.hpp rename to include/metall/kernel/segment_storage.hpp index 3017476f..7ec19861 100644 --- a/include/metall/kernel/segment_storage/mmap_segment_storage.hpp +++ b/include/metall/kernel/segment_storage.hpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (Apache-2.0 OR MIT) -#ifndef METALL_KERNEL_SEGMENT_STORAGE_MMAP_SEGMENT_STORAGE_HPP -#define METALL_KERNEL_SEGMENT_STORAGE_MMAP_SEGMENT_STORAGE_HPP +#ifndef METALL_KERNEL_SEGMENT_STORAGE_HPP +#define METALL_KERNEL_SEGMENT_STORAGE_HPP #include #include @@ -13,28 +13,36 @@ #include #include -#include -#include -#include -#include -#include +#include "metall/defs.hpp" +#include "metall/detail/file.hpp" +#include "metall/detail/file_clone.hpp" +#include "metall/detail/mmap.hpp" +#include "metall/detail/utilities.hpp" +#include "metall/logger.hpp" +#include "metall/kernel/storage.hpp" +#include "metall/kernel/segment_header.hpp" -namespace metall { -namespace kernel { +namespace metall::kernel { namespace { namespace mdtl = metall::mtlldetail; } -/// \brief Segment storage that uses multiple backing files -/// The current implementation does not delete files even though that are empty -template -class mmap_segment_storage { +class segment_storage { + private: + static constexpr const char *k_dir_name = "segment"; + +#ifndef METALL_SEGMENT_BLOCK_SIZE +#error "METALL_SEGMENT_BLOCK_SIZE is not defined." +#endif + // TODO: check block size is a multiple of page size + static constexpr std::size_t k_block_size = METALL_SEGMENT_BLOCK_SIZE; + public: - // -------------------- // - // Constructor & assign operator - // -------------------- // - mmap_segment_storage() { + using path_type = storage::path_type; + using segment_header_type = segment_header; + + segment_storage() { #ifdef METALL_USE_ANONYMOUS_NEW_MAP logger::out(logger::level::info, __FILE__, __LINE__, "METALL_USE_ANONYMOUS_NEW_MAP is defined"); @@ -45,12 +53,12 @@ class mmap_segment_storage { } } - ~mmap_segment_storage() { + ~segment_storage() { int ret = true; if (is_open()) { ret &= sync(true); if (!ret) { - ret &= destroy(); + ret &= release(); } } @@ -60,20 +68,21 @@ class mmap_segment_storage { } } - mmap_segment_storage(const mmap_segment_storage &) = delete; - mmap_segment_storage &operator=(const mmap_segment_storage &) = delete; + segment_storage(const segment_storage &) = delete; + segment_storage &operator=(const segment_storage &) = delete; - mmap_segment_storage(mmap_segment_storage &&other) noexcept + segment_storage(segment_storage &&other) noexcept : m_system_page_size(other.m_system_page_size), m_num_blocks(other.m_num_blocks), m_vm_region_size(other.m_vm_region_size), m_current_segment_size(other.m_current_segment_size), + m_vm_region(other.m_vm_region), m_segment(other.m_segment), - m_base_path(other.m_base_path), + m_segment_header(other.m_segment_header), + m_top_path(other.m_top_path), m_read_only(other.m_read_only), m_free_file_space(other.m_free_file_space), - m_block_fd_list(std::move(other.m_block_fd_list)), - m_block_size(other.m_block_size) + m_block_fd_list(std::move(other.m_block_fd_list)) #ifdef METALL_USE_ANONYMOUS_NEW_MAP , m_anonymous_map_flag_list(other.m_anonymous_map_flag_list) @@ -82,17 +91,18 @@ class mmap_segment_storage { other.priv_set_broken_status(); } - mmap_segment_storage &operator=(mmap_segment_storage &&other) noexcept { + segment_storage &operator=(segment_storage &&other) noexcept { m_system_page_size = other.m_system_page_size; m_num_blocks = other.m_num_blocks; m_vm_region_size = other.m_vm_region_size; m_current_segment_size = other.m_current_segment_size; + m_vm_region = other.m_vm_region; + m_segment_header = other.m_segment_header; m_segment = other.m_segment; - m_base_path = std::move(other.m_base_path); + m_top_path = std::move(other.m_top_path); m_read_only = other.m_read_only; m_free_file_space = other.m_free_file_space; m_block_fd_list = std::move(other.m_block_fd_list); - m_block_size = other.m_block_size; #ifdef METALL_USE_ANONYMOUS_NEW_MAP m_anonymous_map_flag_list = std::move(other.m_anonymous_map_flag_list); #endif @@ -100,35 +110,6 @@ class mmap_segment_storage { return (*this); } - // -------------------- // - // Public methods - // -------------------- // - - /// \brief Gets the size of an existing segment. - /// This is a static version of size() method. - /// \param base_path A path to a segment. - static size_type get_size(const std::string &base_path) { - int block_no = 0; - size_type total_file_size = 0; - while (true) { - const auto file_name = priv_make_block_file_name(base_path, block_no); - if (!mdtl::file_exist(file_name)) { - break; - } - total_file_size += mdtl::get_file_size(file_name); - ++block_no; - } - return total_file_size; - } - - /// \brief Checks if a segment is openable. - /// \param base_path A path to a segment. - /// \return Return true if success; otherwise, false. - static bool openable(const std::string &base_path) { - const auto file_name = priv_make_block_file_name(base_path, 0); - return mdtl::file_exist(file_name); - } - /// \brief Copies segment to another location. /// \param source_path A path to a source segment. /// \param destination_path A destination path. @@ -136,45 +117,48 @@ class mmap_segment_storage { /// \param max_num_threads The maximum number of threads to use. /// If <= 0 is given, the value is automatically determined. /// \return Return true if success; otherwise, false. - static bool copy(const std::string &source_path, - const std::string &destination_path, const bool clone, + static bool copy(const path_type &source_path, + const path_type &destination_path, const bool clone, const int max_num_threads) { - return priv_copy(source_path, destination_path, clone, max_num_threads); + return priv_copy(priv_top_dir_path(source_path), + priv_top_dir_path(destination_path), clone, + max_num_threads); } /// \brief Creates a new segment. /// Calling this function fails if this class already manages an opened - /// segment. \base_path A base directory path to create a segment. \param - /// vm_region_size The size of a reserved VM region. \param vm_region The - /// address of a reserved VM region. \block_size The block size. \return + /// segment. + /// \base_path A base directory path to create a segment. + /// \param capacity A segment capacity to reserve. /// Return true if success; otherwise, false. - bool create(const std::string &base_path, const size_type vm_region_size, - void *const vm_region, const size_type block_size) { - return priv_create(base_path, vm_region_size, vm_region, block_size); + bool create(const path_type &base_path, const std::size_t capacity) { + return priv_create(priv_top_dir_path(base_path), capacity); } /// \brief Opens an existing segment. /// Calling this function fails if this class already manages an opened - /// segment. \base_path A base directory path to create a segment. \param - /// vm_region_size The size of a VM region. \param vm_region The address of a - /// VM region. \param read_only If true, this segment is read only. \return - /// Return true if success; otherwise, false. - bool open(const std::string &base_path, const size_type vm_region_size, - void *const vm_region, const bool read_only) { - return priv_open(base_path, vm_region_size, vm_region, read_only); + /// segment. + /// \param base_path A base directory path to open a segment. + /// \param capacity A segment capacity to reserve. + /// This value will is ignored if read_only is true. + /// \param read_only If true, this segment is read only. + /// \return Return true if success; otherwise, false. + bool open(const path_type &base_path, const std::size_t capacity, + const bool read_only) { + return priv_open(priv_top_dir_path(base_path), capacity, read_only); } /// \brief Extends the currently opened segment if necessary. /// \param request_size A segment size to extend to. /// \return Returns true if the segment is extended to or already larger than /// the requested size. Returns false on failure. - bool extend(const size_type request_size) { + bool extend(const std::size_t request_size) { return priv_extend(request_size); } - /// \brief Destroys the segment --- the data will be lost. + /// \brief Releases the segment --- the data will be lost. /// To save data to files, sync() must be called beforehand. - bool destroy() { return priv_destroy_segment(); } + bool release() { return priv_release_segment(); } /// \brief Syncs the segment with backing files. /// \param sync If false is specified, this function returns before finishing @@ -185,22 +169,47 @@ class mmap_segment_storage { /// The actual behavior depends on the running system. /// \param offset An offset to the region from the beginning of the segment. /// \param nbytes The size of the region. - bool free_region(const different_type offset, const size_type nbytes) { + bool free_region(const std::ptrdiff_t offset, const std::size_t nbytes) { return priv_free_region( offset, nbytes); // Failing this operation is not a critical error } + /// \brief Takes a snapshot of the segment. + /// \param snapshot_path A path to a snapshot. + /// \param clone If true, uses clone (reflink) for copying files. + /// \param max_num_threads The maximum number of threads to use. + /// If <= 0 is given, the value is automatically determined. + /// \return Return true if success; otherwise, false. + bool snapshot(const path_type &snapshot_path, const bool clone, + const int max_num_threads) { + sync(true); + return priv_copy(m_top_path, priv_top_dir_path(snapshot_path), clone, + max_num_threads); + } + /// \brief Returns the address of the segment. /// \return The address of the segment. void *get_segment() const { return m_segment; } - /// \brief Returns the current size. + /// \brief Returns a reference to the segment header. + /// \return A reference to the segment header. + segment_header_type &get_segment_header() { + return *reinterpret_cast(m_vm_region); + } + + /// \brief Returns a reference to the segment header. + /// \return A reference to the segment header. + const segment_header_type &get_segment_header() const { + return *reinterpret_cast(m_vm_region); + } + + /// \brief Returns the current segment size. /// \return The current segment size. - size_type size() const { return m_current_segment_size; } + std::size_t size() const { return m_current_segment_size; } - /// \brief Returns the page size. + /// \brief Returns the underlying page size. /// \return The page size of the system. - size_type page_size() const { return m_system_page_size; } + std::size_t page_size() const { return m_system_page_size; } /// \brief Checks if the segment is read only. /// \return Returns true if the segment is read only; otherwise, returns @@ -211,22 +220,44 @@ class mmap_segment_storage { /// \return Returns true if there is a segment already open. bool is_open() const { return priv_is_open(); } - /// \brief Checks the sanity. + /// \brief Checks the sanity of the instance. /// \return Returns true if there is no issue; otherwise, returns false. /// If false is returned, the instance of this class cannot be used anymore. bool check_sanity() const { return !m_broken; } private: - // -------------------- // - // Private types and static values - // -------------------- // + static path_type priv_top_dir_path(const path_type &base_path) { + return storage::get_path(base_path, k_dir_name); + } - // -------------------- // - // Private methods - // -------------------- // - static std::string priv_make_block_file_name(const std::string &base_path, - const size_type n) { - return base_path + "/block-" + std::to_string(n); + /// \warning This function takes 'top_path' as an argument instead of + /// 'base_path'. + static path_type priv_block_file_path(const path_type &top_path, + const std::size_t n) { + return top_path / ("block-" + std::to_string(n)); + } + + static bool priv_openable(const path_type &top_path) { + const auto file_name = priv_block_file_path(top_path, 0); + return mdtl::file_exist(file_name); + } + + static std::size_t priv_get_size(const path_type &top_path) { + int block_no = 0; + std::size_t total_file_size = 0; + while (true) { + const auto file_name = priv_block_file_path(top_path, block_no); + if (!mdtl::file_exist(file_name)) { + break; + } + total_file_size += mdtl::get_file_size(file_name); + ++block_no; + } + return total_file_size; + } + + std::size_t priv_aligment() const { + return std::max((size_t)m_system_page_size, (size_t)k_block_size); } void priv_clear_status() { @@ -234,7 +265,9 @@ class mmap_segment_storage { m_num_blocks = 0; m_vm_region_size = 0; m_current_segment_size = 0; + m_vm_region = nullptr; m_segment = nullptr; + m_segment_header = nullptr; // m_read_only must not be modified here. } @@ -245,29 +278,29 @@ class mmap_segment_storage { bool priv_is_open() const { return (check_sanity() && m_system_page_size > 0 && m_num_blocks > 0 && - m_vm_region_size > 0 && m_current_segment_size > 0 && m_segment && - !m_base_path.empty() && !m_block_fd_list.empty() && - m_block_size > 0); + m_vm_region_size > 0 && m_current_segment_size > 0 && m_vm_region && + m_segment && !m_top_path.empty() && !m_block_fd_list.empty()); } - static bool priv_copy(const std::string &source_path, - const std::string &destination_path, const bool clone, + static bool priv_copy(const path_type &source_path, + const path_type &destination_path, const bool clone, const int max_num_threads) { if (!mdtl::directory_exist(destination_path)) { if (!mdtl::create_directory(destination_path)) { - std::string s("Cannot create a directory: " + destination_path); + std::string s("Cannot create a directory: " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } } if (clone) { - std::string s("Clone: " + source_path); + std::string s("Clone: " + source_path.string()); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); return mdtl::clone_files_in_directory_in_parallel( source_path, destination_path, max_num_threads); } else { - std::string s("Copy: " + source_path); + std::string s("Copy: " + source_path.string()); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); return mdtl::copy_files_in_directory_in_parallel( source_path, destination_path, max_num_threads); @@ -276,47 +309,121 @@ class mmap_segment_storage { return false; } - bool priv_create(const std::string &base_path, const size_type vm_region_size, - void *const vm_region, const size_type block_size) { + bool priv_reserve_vm(const std::size_t nbytes) { + m_vm_region_size = + mdtl::round_up((int64_t)nbytes, (int64_t)priv_aligment()); + m_vm_region = + mdtl::reserve_aligned_vm_region(priv_aligment(), m_vm_region_size); + + if (!m_vm_region) { + std::stringstream ss; + ss << "Cannot reserve a VM region " << nbytes << " bytes"; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + m_vm_region_size = 0; + return false; + } + assert(reinterpret_cast(m_vm_region) % priv_aligment() == 0); + + return true; + } + + bool priv_release_vm_region() { + // Overwrite the region with PROT_NONE to destroy the map. Because munmap(2) + // synchronizes the map region with the file system, overwriting with + // PROT_NONE beforehand makes munmap(2) fast. Ignore the success/failure of + // this operation as this operation is just a performance optimization. + mdtl::map_with_prot_none(m_segment, m_current_segment_size); + + if (!mdtl::munmap(m_vm_region, m_vm_region_size, false)) { + std::stringstream ss; + ss << "Cannot release a VM region " << (uint64_t)m_vm_region << ", " + << m_vm_region_size << " bytes."; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return false; + } + m_vm_region = nullptr; + m_vm_region_size = 0; + + return true; + } + + bool priv_construct_segment_header(void *const addr) { + if (!addr) { + return false; + } + + const auto size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + if (mdtl::map_anonymous_write_mode(addr, size, MAP_FIXED) != addr) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Cannot allocate segment header"); + return false; + } + m_segment_header = reinterpret_cast(addr); + + new (m_segment_header) segment_header_type(); + + return true; + } + + bool priv_deallocate_segment_header() { + std::destroy_at(&m_segment_header); + const auto size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + const auto ret = mdtl::munmap(m_segment_header, size, false); + m_segment_header = nullptr; + if (!ret) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to deallocate segment header"); + } + return ret; + } + + bool priv_create(const path_type &top_path, const std::size_t capacity) { if (!check_sanity()) return false; if (is_open()) return false; // Cannot open multiple segments simultaneously. { - std::string s("Create a segment under: " + base_path); + std::string s("Create a segment under: " + top_path.string()); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); } - if (!mdtl::directory_exist(base_path)) { - if (!mdtl::create_directory(base_path)) { - std::string s("Cannot create a directory: " + base_path); + if (!mdtl::directory_exist(top_path)) { + if (!mdtl::create_directory(top_path)) { + std::string s("Cannot create a directory: " + top_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); // As no internal value has been changed, m_broken is still false. return false; } } - m_block_size = - mdtl::round_up(std::min(vm_region_size, block_size), page_size()); - m_base_path = base_path; - m_vm_region_size = mdtl::round_down(vm_region_size, page_size()); - m_segment = reinterpret_cast( - mdtl::round_up(reinterpret_cast(vm_region), page_size())); + const auto header_size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + const auto vm_region_size = header_size + capacity; + if (!priv_reserve_vm(vm_region_size)) { + priv_set_broken_status(); + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + priv_construct_segment_header(m_vm_region); + + m_top_path = top_path; m_read_only = false; // Create the first block so that we can assume that there is a block always // in a segment. - if (!priv_create_new_map(m_base_path, 0, m_block_size, 0)) { + if (!priv_create_new_map(m_top_path, 0, k_block_size, 0)) { priv_set_broken_status(); return false; } - m_current_segment_size = m_block_size; + m_current_segment_size = k_block_size; m_num_blocks = 1; - if (!priv_test_file_space_free(base_path)) { - std::string s("Failed to test file space free: " + base_path); + if (!priv_test_file_space_free(top_path)) { + std::string s("Failed to test file space free: " + top_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } @@ -324,50 +431,57 @@ class mmap_segment_storage { return true; } - bool priv_open(const std::string &base_path, const size_type vm_region_size, - void *const vm_region, const bool read_only) { + bool priv_open(const path_type &top_path, const std::size_t capacity, + const bool read_only) { if (!check_sanity()) return false; if (is_open()) return false; // Cannot open multiple segments simultaneously. { - std::string s("Open a segment under: " + base_path); + std::string s("Open a segment under: " + top_path.string()); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); } - m_base_path = base_path; - m_vm_region_size = mdtl::round_down(vm_region_size, page_size()); - m_segment = reinterpret_cast( - mdtl::round_up(reinterpret_cast(vm_region), page_size())); + const auto header_size = + mdtl::round_up(sizeof(segment_header_type), int64_t(priv_aligment())); + const auto vm_size = + header_size + ((read_only) ? priv_get_size(top_path) : capacity); + if (!priv_reserve_vm(vm_size)) { + priv_set_broken_status(); + return false; + } + m_segment = reinterpret_cast(m_vm_region) + header_size; + priv_construct_segment_header(m_vm_region); + + m_top_path = top_path; m_read_only = read_only; // Maps block files one by one m_num_blocks = 0; while (true) { - const auto file_name = - priv_make_block_file_name(m_base_path, m_num_blocks); + const auto file_name = priv_block_file_path(m_top_path, m_num_blocks); if (!mdtl::file_exist(file_name)) { break; // Mapped all files } const auto file_size = mdtl::get_file_size(file_name); assert(file_size % page_size() == 0); - if (m_block_size > 0 && m_block_size != (size_type)file_size) { + if (k_block_size != (std::size_t)file_size) { logger::out(logger::level::error, __FILE__, __LINE__, "File sizes are not the same"); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } - m_block_size = file_size; - const auto fd = priv_map_file(file_name, m_block_size, - m_current_segment_size, read_only); + const auto fd = + priv_map_file(file_name, k_block_size, + std::ptrdiff_t(m_current_segment_size), read_only); if (fd == -1) { std::stringstream ss; ss << "Failed to map a file " << file_name; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } @@ -375,14 +489,14 @@ class mmap_segment_storage { #ifdef METALL_USE_ANONYMOUS_NEW_MAP m_anonymous_map_flag_list.push_back(false); #endif - m_current_segment_size += m_block_size; + m_current_segment_size += k_block_size; ++m_num_blocks; } - if (!read_only && !priv_test_file_space_free(base_path)) { - std::string s("Failed to test file space free: " + base_path); + if (!read_only && !priv_test_file_space_free(m_top_path)) { + std::string s("Failed to test file space free: " + m_top_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } @@ -395,7 +509,7 @@ class mmap_segment_storage { return true; } - bool priv_extend(const size_type request_size) { + bool priv_extend(const std::size_t request_size) { if (!is_open()) return false; if (m_read_only) { @@ -413,37 +527,36 @@ class mmap_segment_storage { } while (m_current_segment_size < request_size) { - if (!priv_create_new_map(m_base_path, m_num_blocks, m_block_size, - m_current_segment_size)) { + if (!priv_create_new_map(m_top_path, m_num_blocks, k_block_size, + std::ptrdiff_t(m_current_segment_size))) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to extend the segment"); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } ++m_num_blocks; - m_current_segment_size += m_block_size; + m_current_segment_size += k_block_size; } return true; } - bool priv_create_new_map(const std::string &base_path, - const size_type block_number, - const size_type file_size, - const different_type segment_offset) { - const std::string file_name = - priv_make_block_file_name(base_path, block_number); + bool priv_create_new_map(const path_type &top_path, + const std::size_t block_number, + const std::size_t file_size, + const std::ptrdiff_t segment_offset) { + const path_type file_name = priv_block_file_path(top_path, block_number); { - std::string s("Create and extend a file " + file_name + " with " + - std::to_string(file_size) + " bytes"); + std::string s("Create and extend a file " + file_name.string() + + " with " + std::to_string(file_size) + " bytes"); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); } if (!mdtl::create_file(file_name)) return false; if (!mdtl::extend_file_size(file_name, file_size)) return false; - if (static_cast(mdtl::get_file_size(file_name)) < file_size) { - std::string s("Failed to create and extend file: " + file_name); + if (static_cast(mdtl::get_file_size(file_name)) < file_size) { + std::string s("Failed to create and extend file: " + file_name.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -468,8 +581,8 @@ class mmap_segment_storage { return true; } - int priv_map_file(const std::string &path, const size_type file_size, - const different_type segment_offset, + int priv_map_file(const path_type &path, const std::size_t file_size, + const std::ptrdiff_t segment_offset, const bool read_only) const { assert(!path.empty()); assert(file_size > 0); @@ -499,7 +612,7 @@ class mmap_segment_storage { : mdtl::map_file_write_mode(path, map_addr, file_size, 0, MAP_FIXED | map_nosync); if (ret.first == -1 || !ret.second) { - std::string s("Failed to map a file: " + path); + std::string s("Failed to map a file: " + path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); if (ret.first != -1) { mdtl::os_close(ret.first); @@ -510,8 +623,8 @@ class mmap_segment_storage { return ret.first; } - int priv_map_anonymous(const std::string &path, const size_type region_size, - const different_type segment_offset) const { + int priv_map_anonymous(const path_type &path, const std::size_t region_size, + const std::ptrdiff_t segment_offset) const { assert(!path.empty()); assert(region_size > 0); assert(segment_offset >= 0); @@ -539,7 +652,7 @@ class mmap_segment_storage { const auto fd = ::open(path.c_str(), O_RDWR); if (fd == -1) { logger::perror(logger::level::error, __FILE__, __LINE__, "open"); - std::string s("Failed to open a file " + path); + std::string s("Failed to open a file " + path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); // Destroy the map by overwriting PROT_NONE map since the VM region is // managed by another class. @@ -551,7 +664,7 @@ class mmap_segment_storage { return fd; } - bool priv_destroy_segment() { + bool priv_release_segment() { if (!is_open()) return false; int succeeded = true; @@ -559,13 +672,17 @@ class mmap_segment_storage { succeeded &= mdtl::os_close(fd); } - // Destroy the mapping region by calling mmap with PROT_NONE over the - // region. As the unmap system call syncs the data first, this approach is - // significantly fast. - succeeded &= mdtl::map_with_prot_none(m_segment, m_current_segment_size); - // NOTE: the VM region will be unmapped by another class + succeeded &= priv_deallocate_segment_header(); - priv_clear_status(); + succeeded &= priv_release_vm_region(); + + if (!succeeded) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to release the segment"); + priv_set_broken_status(); + } else { + priv_clear_status(); + } return succeeded; } @@ -623,8 +740,8 @@ class mmap_segment_storage { } #endif const auto map = - static_cast(m_segment) + block_no * m_block_size; - num_successes.fetch_add(mdtl::os_msync(map, m_block_size, sync) ? 1 + static_cast(m_segment) + block_no * k_block_size; + num_successes.fetch_add(mdtl::os_msync(map, k_block_size, sync) ? 1 : 0); } else { break; @@ -652,14 +769,14 @@ class mmap_segment_storage { return num_successes == m_block_fd_list.size(); } - bool priv_free_region(const different_type offset, - const size_type nbytes) const { + bool priv_free_region(const std::ptrdiff_t offset, + const std::size_t nbytes) const { if (!is_open() || m_read_only) return false; if (offset + nbytes > m_current_segment_size) return false; #ifdef METALL_USE_ANONYMOUS_NEW_MAP - const auto block_no = offset / m_block_size; + const auto block_no = offset / k_block_size; assert(m_anonymous_map_flag_list.size() > block_no); if (m_anonymous_map_flag_list[block_no]) { return priv_uncommit_private_anonymous_pages(offset, nbytes); @@ -672,38 +789,38 @@ class mmap_segment_storage { return priv_uncommit_pages(offset, nbytes); } - bool priv_uncommit_pages_and_free_file_space(const different_type offset, - const size_type nbytes) const { + bool priv_uncommit_pages_and_free_file_space(const std::ptrdiff_t offset, + const std::size_t nbytes) const { return mdtl::uncommit_shared_pages_and_free_file_space( static_cast(m_segment) + offset, nbytes); } - bool priv_uncommit_pages(const different_type offset, - const size_type nbytes) const { + bool priv_uncommit_pages(const std::ptrdiff_t offset, + const std::size_t nbytes) const { return mdtl::uncommit_shared_pages(static_cast(m_segment) + offset, nbytes); } - bool priv_uncommit_private_anonymous_pages(const different_type offset, - const size_type nbytes) const { + bool priv_uncommit_private_anonymous_pages(const std::ptrdiff_t offset, + const std::size_t nbytes) const { return mdtl::uncommit_private_anonymous_pages( static_cast(m_segment) + offset, nbytes); } #ifdef METALL_USE_ANONYMOUS_NEW_MAP - bool priv_sync_anonymous_map(const size_type block_no) { + bool priv_sync_anonymous_map(const std::size_t block_no) { assert(m_anonymous_map_flag_list[block_no]); { std::string s("Sync anonymous map at block " + std::to_string(block_no)); logger::out(logger::level::info, __FILE__, __LINE__, s.c_str()); } - auto *const addr = static_cast(m_segment) + block_no * m_block_size; - if (::write(m_block_fd_list[block_no], addr, m_block_size) != - (ssize_t)m_block_size) { + auto *const addr = static_cast(m_segment) + block_no * k_block_size; + if (::write(m_block_fd_list[block_no], addr, k_block_size) != + (ssize_t)k_block_size) { std::string s("Failed to write back a block"); logger::perror(logger::level::error, __FILE__, __LINE__, s.c_str()); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } @@ -721,12 +838,12 @@ class mmap_segment_storage { 0; #endif const auto mapped_addr = - mdtl::map_file_write_mode(m_block_fd_list[block_no], addr, m_block_size, + mdtl::map_file_write_mode(m_block_fd_list[block_no], addr, k_block_size, 0, MAP_FIXED | map_nosync); if (!mapped_addr || mapped_addr != addr) { std::string s("Failed to map a block"); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - priv_destroy_segment(); + priv_release_segment(); priv_set_broken_status(); return false; } @@ -744,24 +861,25 @@ class mmap_segment_storage { return true; } - bool priv_test_file_space_free(const std::string &base_path) { + bool priv_test_file_space_free(const path_type &top_path) { #ifdef METALL_DISABLE_FREE_FILE_SPACE m_free_file_space = false; return true; #endif assert(m_system_page_size > 0); - const std::string file_path(base_path + "/test"); - const size_type file_size = m_system_page_size * 2; + const path_type file_path(top_path.string() + "/test"); + const std::size_t file_size = m_system_page_size * 2; if (!mdtl::create_file(file_path)) return false; if (!mdtl::extend_file_size(file_path, file_size)) return false; - assert(static_cast(mdtl::get_file_size(file_path)) >= file_size); + assert(static_cast(mdtl::get_file_size(file_path)) >= + file_size); const auto ret = mdtl::map_file_write_mode(file_path, nullptr, file_size, 0); if (ret.first == -1 || !ret.second) { - std::string s("Failed to map file: " + file_path); + std::string s("Failed to map file: " + file_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); if (ret.first != -1) mdtl::os_close(ret.first); return false; @@ -778,7 +896,7 @@ class mmap_segment_storage { } if (!mdtl::os_close(ret.first)) { - std::string s("Failed to close file: " + file_path); + std::string s("Failed to close file: " + file_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -786,7 +904,7 @@ class mmap_segment_storage { // Closing mdtl::munmap(ret.second, file_size, false); if (!mdtl::remove_file(file_path)) { - std::string s("Failed to remove a file: " + file_path); + std::string s("Failed to remove a file: " + file_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; } @@ -804,21 +922,22 @@ class mmap_segment_storage { // Private fields // -------------------- // ssize_t m_system_page_size{0}; - size_type m_num_blocks{0}; - size_type m_vm_region_size{0}; - size_type m_current_segment_size{0}; + std::size_t m_num_blocks{0}; + std::size_t m_vm_region_size{0}; + std::size_t m_current_segment_size{0}; + void *m_vm_region{nullptr}; void *m_segment{nullptr}; - std::string m_base_path; + segment_header_type *m_segment_header{nullptr}; + path_type m_top_path; bool m_read_only{false}; bool m_free_file_space{true}; std::vector m_block_fd_list; - size_type m_block_size{0}; bool m_broken{false}; #ifdef METALL_USE_ANONYMOUS_NEW_MAP std::vector m_anonymous_map_flag_list; #endif }; -} // namespace kernel -} // namespace metall -#endif // METALL_KERNEL_SEGMENT_STORAGE_MMAP_SEGMENT_STORAGE_HPP +} // namespace metall::kernel + +#endif // METALL_KERNEL_SEGMENT_STORAGE_HPP diff --git a/include/metall/kernel/storage.hpp b/include/metall/kernel/storage.hpp new file mode 100644 index 00000000..b74656c6 --- /dev/null +++ b/include/metall/kernel/storage.hpp @@ -0,0 +1,82 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#pragma once + +#include +#include +#include + +#include +#include + +namespace metall::kernel { + +namespace { +namespace fs = std::filesystem; +namespace mdtl = metall::mtlldetail; +} // namespace + +/// \brief Manage directory structure of a datastore. +class storage { + public: + using path_type = std::filesystem::path; + + static path_type get_path(const path_type &base_path, const path_type &key) { + return get_path(base_path, {key}); + } + + static path_type get_path(const path_type &base_path, + const std::initializer_list &paths) { + auto path = priv_get_root_path(base_path); + for (const auto &p : paths) { + path /= p; + } + return path; + } + + /// \brief Create a new datastore. If a datastore already exists, remove it + /// and create a new one. + /// \param base_path A path to a directory where a datastore is created. + /// \return On success, returns true. On error, returns false. + static bool create(const path_type &base_path) { + const auto root_dir = priv_get_root_path(base_path); + + // Remove existing directory to certainly create a new data store + if (!mdtl::remove_file(root_dir)) { + std::string s("Failed to remove a directory: " + root_dir.string()); + logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); + return false; + } + + if (!mdtl::create_directory(root_dir)) { + std::string s("Failed to create directory: " + root_dir.string()); + logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); + return false; + } + + return true; + } + + /// \brief Remove a datastore. + /// \param base_path A path to an existing datastore. + /// \return On success, returns true. On error, returns false. + static bool remove(const path_type &base_path) { + const auto root_dir = priv_get_root_path(base_path); + if (!mdtl::remove_file(root_dir)) { + std::string s("Failed to remove a directory: " + root_dir.string()); + logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); + return false; + } + return true; + } + + private: + static path_type priv_get_root_path(const path_type &base_path) { + return base_path / "mds"; + } +}; + +} // namespace metall::kernel \ No newline at end of file diff --git a/include/metall/metall.hpp b/include/metall/metall.hpp index af47f294..98254450 100644 --- a/include/metall/metall.hpp +++ b/include/metall/metall.hpp @@ -11,13 +11,28 @@ #include #include +#if defined(METALL_USE_UMAP) && defined(METALL_USE_PRIVATEER) +#error \ + "METALL_USE_UMAP and METALL_USE_PRIVATEER cannot be defined at the same time" +#endif + +#ifdef METALL_USE_PRIVATEER +#include +#endif + +#ifdef METALL_USE_UMAP +#include +#endif + /// \namespace metall /// \brief The top level of namespace of Metall namespace metall { +#if !(defined(METALL_USE_PRIVATEER) || defined(METALL_USE_UMAP)) /// \brief Default Metall manager class which is an alias of basic_manager with /// the default template parameters. using manager = basic_manager<>; +#endif } // namespace metall diff --git a/include/metall/stl_allocator.hpp b/include/metall/stl_allocator.hpp index eb1297d1..d7aaff7f 100644 --- a/include/metall/stl_allocator.hpp +++ b/include/metall/stl_allocator.hpp @@ -165,7 +165,7 @@ class stl_allocator { "nullptr: cannot access to manager kernel"); throw std::bad_alloc(); } - auto manager_kernel = *get_pointer_to_manager_kernel(); + auto* manager_kernel = *get_pointer_to_manager_kernel(); if (!manager_kernel) { logger::out(logger::level::error, __FILE__, __LINE__, "nullptr: cannot access to manager kernel"); diff --git a/test/container/stl_allocator_test.cpp b/test/container/stl_allocator_test.cpp index 7935186a..1a3759fd 100644 --- a/test/container/stl_allocator_test.cpp +++ b/test/container/stl_allocator_test.cpp @@ -134,13 +134,11 @@ TEST(StlAllocatorTest, Exception) { alloc_type allocator = manager.get_allocator(); - ASSERT_NO_THROW({ allocator.deallocate(allocator.allocate(1), 1); }); - // Turn off log temporary because the following exception test cases could // show error messages metall::logger::set_log_level(metall::logger::level::critical); - ASSERT_THROW({ allocator.allocate(1UL << 24UL); }, std::bad_alloc); + ASSERT_NO_THROW({ allocator.deallocate(allocator.allocate(1), 1); }); ASSERT_THROW({ allocator.allocate(allocator.max_size() + 1); }, std::bad_array_new_length); diff --git a/test/kernel/CMakeLists.txt b/test/kernel/CMakeLists.txt index 20604e2b..c28d067a 100644 --- a/test/kernel/CMakeLists.txt +++ b/test/kernel/CMakeLists.txt @@ -17,7 +17,7 @@ target_compile_definitions(manager_test_single_thread PRIVATE "METALL_DISABLE_CO add_metall_test_executable(snapshot_test snapshot_test.cpp) -add_metall_test_executable(copy_file_test copy_file_test.cpp) +add_metall_test_executable(copy_datastore_test copy_datastore_test.cpp) include(setup_omp) if (OpenMP_CXX_FOUND) @@ -29,6 +29,6 @@ endif() add_metall_test_executable(multimanager_test multimanager_test.cpp) -add_metall_test_executable(mmap_segment_storage_test mmap_segment_storage_test.cpp) +add_metall_test_executable(segment_storage_test segment_storage_test.cpp) add_metall_test_executable(object_attribute_accessor_test object_attribute_accessor_test.cpp) \ No newline at end of file diff --git a/test/kernel/copy_file_test.cpp b/test/kernel/copy_datastore_test.cpp similarity index 85% rename from test/kernel/copy_file_test.cpp rename to test/kernel/copy_datastore_test.cpp index 6a561f16..70a39f15 100644 --- a/test/kernel/copy_file_test.cpp +++ b/test/kernel/copy_datastore_test.cpp @@ -19,6 +19,15 @@ void create(const std::string &dir_path) { manager.construct("b")(2); } +void modify(const std::string &dir_path) { + metall::manager manager(metall::open_only, dir_path.c_str()); + + auto a = manager.find("a").first; + *a = 10; + auto b = manager.find("b").first; + *b = 20; +} + void open(const std::string &dir_path) { metall::manager manager(metall::open_read_only, dir_path.c_str()); @@ -48,6 +57,8 @@ TEST(CopyFileTest, SyncCopy) { ASSERT_TRUE(metall::manager::copy(original_dir_path().c_str(), copy_dir_path().c_str())); + modify(original_dir_path()); + open(copy_dir_path()); } @@ -61,6 +72,8 @@ TEST(CopyFileTest, AsyncCopy) { copy_dir_path().c_str()); ASSERT_TRUE(handler.get()); + modify(original_dir_path()); + open(copy_dir_path()); } } // namespace \ No newline at end of file diff --git a/test/kernel/manager_multithread_test.cpp b/test/kernel/manager_multithread_test.cpp index ac742faf..46138ed4 100644 --- a/test/kernel/manager_multithread_test.cpp +++ b/test/kernel/manager_multithread_test.cpp @@ -29,10 +29,8 @@ namespace omp = metall::utility::omp; // -------------------- // // Manage Type // -------------------- // -using chunk_no_type = uint32_t; -static constexpr std::size_t k_chunk_size = 1ULL << 21; constexpr std::size_t k_min_object_size = 8; // TODO: take from the file -using manager_type = metall::basic_manager; +using manager_type = metall::basic_manager<>; template using allocator_type = typename manager_type::allocator_type; @@ -94,7 +92,7 @@ int get_num_threads() { /// \brief /// This validation fails if the total allocation size of any size is less than -/// k_chunk_size +/// manager_type::chunk_size() template void run_alloc_dealloc_separated_test(const list_type &allocation_size_list) { // Allocate manager @@ -128,7 +126,7 @@ void run_alloc_dealloc_separated_test(const list_type &allocation_size_list) { /// \brief /// This validation fails if the total allocation size of any size is less than -/// k_chunk_size +/// manager_type::chunk_size() template void run_alloc_dealloc_mixed_and_write_value_test( const list_type &allocation_size_list) { @@ -209,13 +207,13 @@ TEST(ManagerMultithreadsTest, LargeAllocDeallocSeparated) { std::vector allocation_size_list; allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size); + num_allocations_per_size, manager_type::chunk_size()); allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size * 2); + num_allocations_per_size, manager_type::chunk_size() * 2); allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size * 4); + num_allocations_per_size, manager_type::chunk_size() * 4); allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size * 8); + num_allocations_per_size, manager_type::chunk_size() * 8); shuffle_list(&allocation_size_list); @@ -230,13 +228,13 @@ TEST(ManagerMultithreadsTest, SizeMixedAllocDeallocSeparated) { k_min_object_size * 1); allocation_size_list.insert(allocation_size_list.end(), 1024, k_min_object_size * 2); - allocation_size_list.insert(allocation_size_list.end(), 1024, k_chunk_size); + allocation_size_list.insert(allocation_size_list.end(), 1024, manager_type::chunk_size()); allocation_size_list.insert(allocation_size_list.end(), 1024, - k_chunk_size * 2); + manager_type::chunk_size() * 2); allocation_size_list.insert(allocation_size_list.end(), 1024, - k_chunk_size * 4); + manager_type::chunk_size() * 4); allocation_size_list.insert(allocation_size_list.end(), 1024, - k_chunk_size * 8); + manager_type::chunk_size() * 8); shuffle_list(&allocation_size_list); @@ -262,11 +260,11 @@ TEST(ManagerMultithreadsTest, LargeAllocDeallocMixed) { std::vector allocation_size_list; allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size); + num_allocations_per_size, manager_type::chunk_size()); allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size * 2); + num_allocations_per_size, manager_type::chunk_size() * 2); allocation_size_list.insert(allocation_size_list.end(), - num_allocations_per_size, k_chunk_size * 4); + num_allocations_per_size, manager_type::chunk_size() * 4); shuffle_list(&allocation_size_list); @@ -281,9 +279,9 @@ TEST(ManagerMultithreadsTest, SizeMixedAllocDeallocMixed) { k_min_object_size); allocation_size_list.insert(allocation_size_list.end(), 1024, k_min_object_size * 4); - allocation_size_list.insert(allocation_size_list.end(), 1024, k_chunk_size); + allocation_size_list.insert(allocation_size_list.end(), 1024, manager_type::chunk_size()); allocation_size_list.insert(allocation_size_list.end(), 1024, - k_chunk_size * 4); + manager_type::chunk_size() * 4); shuffle_list(&allocation_size_list); run_alloc_dealloc_mixed_and_write_value_test(allocation_size_list); diff --git a/test/kernel/mmap_segment_storage_test.cpp b/test/kernel/mmap_segment_storage_test.cpp deleted file mode 100644 index e76c8064..00000000 --- a/test/kernel/mmap_segment_storage_test.cpp +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2020 Lawrence Livermore National Security, LLC and other Metall -// Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -#include "gtest/gtest.h" - -#include -#include "../test_utility.hpp" - -namespace { -using segment_storage_type = - metall::kernel::mmap_segment_storage; - -const std::string &test_dir() { - const static std::string path(test_utility::make_test_path()); - return path; -} - -const std::string &test_file_prefix() { - const static std::string path(test_dir() + "/backing_file"); - return path; -} - -void prepare_test_dir() { - ASSERT_TRUE(metall::mtlldetail::remove_file(test_dir())); - ASSERT_TRUE(metall::mtlldetail::create_directory(test_dir())); -} - -TEST(MultifileSegmentStorageTest, PageSize) { - segment_storage_type segment_storage; - ASSERT_GT(segment_storage.page_size(), 0); -} - -TEST(MultifileSegmentStorageTest, Create) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size / 2)); - ASSERT_TRUE(segment_storage.is_open()); - ASSERT_TRUE(segment_storage.check_sanity()); - ASSERT_NE(segment_storage.get_segment(), nullptr); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size / 2; ++i) { - buf[i] = '1'; - ASSERT_EQ(buf[i], '1'); - } - } - - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size * 2)); - ASSERT_TRUE(segment_storage.is_open()); - ASSERT_TRUE(segment_storage.check_sanity()); - ASSERT_NE(segment_storage.get_segment(), nullptr); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size; ++i) { - buf[i] = '1'; - ASSERT_EQ(buf[i], '1'); - } - } - - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); -} - -TEST(MultifileSegmentStorageTest, GetSize) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - - // vm_size < single_file_size - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size / 2)); - ASSERT_GE(segment_storage.size(), vm_size / 2); - ASSERT_GE(segment_storage_type::get_size(test_file_prefix()), vm_size / 2); - } - - // vm_size > single_file_size - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size * 2)); - ASSERT_GE(segment_storage.size(), vm_size); - ASSERT_GE(segment_storage_type::get_size(test_file_prefix()), vm_size); - } - - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); -} - -TEST(MultifileSegmentStorageTest, Extend) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size / 2)); - - // Has enough space already - ASSERT_TRUE(segment_storage.extend(vm_size / 2)); - ASSERT_GE(segment_storage.size(), vm_size / 2); - ASSERT_GE(segment_storage_type::get_size(test_file_prefix()), vm_size / 2); - - // Extend the space - ASSERT_TRUE(segment_storage.extend(vm_size)); - ASSERT_GE(segment_storage.size(), vm_size); - ASSERT_GE(segment_storage_type::get_size(test_file_prefix()), vm_size); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size; ++i) { - buf[i] = '1'; - ASSERT_EQ(buf[i], '1'); - } - } - - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); -} - -TEST(MultifileSegmentStorageTest, Openable) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size)); - } - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); - - ASSERT_TRUE(segment_storage_type::openable(test_file_prefix())); - ASSERT_FALSE(segment_storage_type::openable(test_file_prefix() + "_dummy")); -} - -TEST(MultifileSegmentStorageTest, Open) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - - { - prepare_test_dir(); - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size)); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size; ++i) { - buf[i] = '1'; - ASSERT_EQ(buf[i], '1'); - } - } - - // Open and Update - { - segment_storage_type segment_storage; - ASSERT_TRUE(segment_storage.open(test_file_prefix(), vm_size, addr, false)); - ASSERT_TRUE(segment_storage.is_open()); - ASSERT_TRUE(segment_storage.check_sanity()); - ASSERT_FALSE(segment_storage.read_only()); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size; ++i) { - ASSERT_EQ(buf[i], '1'); - buf[i] = '2'; - } - } - - // Read only - { - segment_storage_type segment_storage; - ASSERT_TRUE(segment_storage.open(test_file_prefix(), vm_size, addr, true)); - ASSERT_TRUE(segment_storage.is_open()); - ASSERT_TRUE(segment_storage.check_sanity()); - ASSERT_TRUE(segment_storage.read_only()); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size; ++i) { - ASSERT_EQ(buf[i], '2'); - } - } - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); -} - -TEST(MultifileSegmentStorageTest, Sync) { - constexpr std::size_t vm_size = 1ULL << 22ULL; - auto addr = metall::mtlldetail::reserve_vm_region(vm_size); - ASSERT_NE(addr, nullptr); - - { - prepare_test_dir(); - - segment_storage_type segment_storage; - ASSERT_TRUE( - segment_storage.create(test_file_prefix(), vm_size, addr, vm_size / 2)); - auto buf = static_cast(segment_storage.get_segment()); - for (std::size_t i = 0; i < vm_size / 2; ++i) { - buf[i] = '1'; - ASSERT_EQ(buf[i], '1'); - } - segment_storage.sync(true); - - for (std::size_t i = 0; i < vm_size / 2; ++i) { - ASSERT_EQ(buf[i], '1'); - } - - segment_storage.extend(vm_size); - for (std::size_t i = 0; i < vm_size; ++i) { - buf[i] = '2'; - } - segment_storage.sync(true); - - for (std::size_t i = 0; i < vm_size; ++i) { - ASSERT_EQ(buf[i], '2'); - } - } - - ASSERT_TRUE(metall::mtlldetail::munmap(addr, vm_size, true)); -} -} // namespace \ No newline at end of file diff --git a/test/kernel/multimanager_test.cpp b/test/kernel/multimanager_test.cpp index 26b4140b..58cd163e 100644 --- a/test/kernel/multimanager_test.cpp +++ b/test/kernel/multimanager_test.cpp @@ -16,10 +16,7 @@ namespace { namespace omp = metall::utility::omp; -using chunk_no_type = uint32_t; -constexpr std::size_t k_chunk_size = 1UL << 21UL; - -using manager_type = metall::basic_manager; +using manager_type = metall::basic_manager<>; template using metall_allocator = typename manager_type::allocator_type; @@ -35,9 +32,9 @@ TEST(MultiManagerTest, SingleThread) { { manager_type manager1(metall::create_only, dir_path1.c_str(), - k_chunk_size * 8); + manager_type::chunk_size() * 8); manager_type manager2(metall::create_only, dir_path2.c_str(), - k_chunk_size * 8); + manager_type::chunk_size() * 8); vector_type *vector1 = manager1.construct("vector")(manager1.get_allocator<>()); @@ -112,7 +109,7 @@ TEST(MultiManagerTest, MultiThread) { "/" + std::to_string(omp::get_thread_num()))); manager_type manager(metall::create_only, dir_path.c_str(), - k_chunk_size * 16); + manager_type::chunk_size() * 16); vector_type *vector = manager.construct("vector")(manager.get_allocator<>()); diff --git a/test/kernel/segment_storage_test.cpp b/test/kernel/segment_storage_test.cpp new file mode 100644 index 00000000..d5692c25 --- /dev/null +++ b/test/kernel/segment_storage_test.cpp @@ -0,0 +1,179 @@ +// Copyright 2020 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include "gtest/gtest.h" + +#include +#include "../test_utility.hpp" + +namespace { +using segment_storage_type = metall::kernel::segment_storage; + +const std::string &test_dir() { + const static std::string path(test_utility::make_test_path()); + return path; +} + +const std::string &test_file_prefix() { + const static std::string path(test_dir() + "/backing_file"); + return path; +} + +void prepare_test_dir() { + ASSERT_TRUE(metall::mtlldetail::remove_file(test_dir())); + ASSERT_TRUE(metall::mtlldetail::create_directory(test_dir())); +} + +TEST(MultifileSegmentStorageTest, PageSize) { + segment_storage_type data_storage; + ASSERT_GT(data_storage.page_size(), 0); +} + +TEST(MultifileSegmentStorageTest, Create) { + constexpr std::size_t vm_size = 1ULL << 22ULL; + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + ASSERT_TRUE(data_storage.is_open()); + ASSERT_TRUE(data_storage.check_sanity()); + ASSERT_NE(data_storage.get_segment(), nullptr); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size / 2; ++i) { + buf[i] = '1'; + ASSERT_EQ(buf[i], '1'); + } + } + + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + ASSERT_TRUE(data_storage.is_open()); + ASSERT_TRUE(data_storage.check_sanity()); + ASSERT_NE(data_storage.get_segment(), nullptr); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size; ++i) { + buf[i] = '1'; + ASSERT_EQ(buf[i], '1'); + } + } +} + +TEST(MultifileSegmentStorageTest, GetSize) { + constexpr std::size_t vm_size = 1ULL << 22ULL; + + // vm_size < single_file_size + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + ASSERT_GE(data_storage.size(), vm_size / 2); + } + + // vm_size > single_file_size + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + ASSERT_GE(data_storage.size(), vm_size); + } +} + +TEST(MultifileSegmentStorageTest, Extend) { + constexpr std::size_t vm_size = 1ULL << 22ULL; + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + + // Has enough space already + ASSERT_TRUE(data_storage.extend(vm_size / 2)); + ASSERT_GE(data_storage.size(), vm_size / 2); + + // Extend the space + ASSERT_TRUE(data_storage.extend(vm_size)); + ASSERT_GE(data_storage.size(), vm_size); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size; ++i) { + buf[i] = '1'; + ASSERT_EQ(buf[i], '1'); + } + } +} + +TEST(MultifileSegmentStorageTest, Open) { + constexpr std::size_t vm_size = 1ULL << 22ULL; + + { + prepare_test_dir(); + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size; ++i) { + buf[i] = '1'; + ASSERT_EQ(buf[i], '1'); + } + } + + // Open and Update + { + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.open(test_file_prefix(), vm_size, false)); + ASSERT_TRUE(data_storage.is_open()); + ASSERT_TRUE(data_storage.check_sanity()); + ASSERT_FALSE(data_storage.read_only()); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size; ++i) { + ASSERT_EQ(buf[i], '1'); + buf[i] = '2'; + } + } + + // Read only + { + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.open(test_file_prefix(), vm_size, true)); + ASSERT_TRUE(data_storage.is_open()); + ASSERT_TRUE(data_storage.check_sanity()); + ASSERT_TRUE(data_storage.read_only()); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size; ++i) { + ASSERT_EQ(buf[i], '2'); + } + } +} + +TEST(MultifileSegmentStorageTest, Sync) { + constexpr std::size_t vm_size = 1ULL << 22ULL; + + { + prepare_test_dir(); + + segment_storage_type data_storage; + ASSERT_TRUE(data_storage.create(test_file_prefix(), vm_size)); + auto buf = static_cast(data_storage.get_segment()); + for (std::size_t i = 0; i < vm_size / 2; ++i) { + buf[i] = '1'; + ASSERT_EQ(buf[i], '1'); + } + data_storage.sync(true); + + for (std::size_t i = 0; i < vm_size / 2; ++i) { + ASSERT_EQ(buf[i], '1'); + } + + data_storage.extend(vm_size); + for (std::size_t i = 0; i < vm_size; ++i) { + buf[i] = '2'; + } + data_storage.sync(true); + + for (std::size_t i = 0; i < vm_size; ++i) { + ASSERT_EQ(buf[i], '2'); + } + } +} +} // namespace \ No newline at end of file From 5b56d151e19310f095c440ed44154a93b9935e63 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Mon, 18 Dec 2023 15:29:42 -0800 Subject: [PATCH 13/26] Remove own file operation implementation Depends on C++17 filesystem --- CMakeLists.txt | 46 ++- bench/bfs/run_bfs_bench_metall.cpp | 2 +- bench/bfs/run_bfs_bench_metall_multiple.cpp | 2 +- .../run_simple_allocation_bench_metall.cpp | 2 +- cmake/check_cxx_filesystem_library.cmake | 25 ++ cmake/include_cxx_filesystem_library.cmake | 40 --- docs/readthedocs/basics/getting_started.md | 17 +- include/metall/basic_manager.hpp | 45 +-- include/metall/detail/file.hpp | 283 ++++++---------- include/metall/detail/file_clone.hpp | 36 ++- include/metall/detail/mmap.hpp | 6 +- include/metall/detail/ptree.hpp | 8 +- .../kernel/attributed_object_directory.hpp | 10 +- include/metall/kernel/bin_directory.hpp | 6 +- include/metall/kernel/chunk_directory.hpp | 8 +- include/metall/kernel/manager_kernel.hpp | 43 +-- include/metall/kernel/manager_kernel_impl.ipp | 36 ++- include/metall/kernel/segment_allocator.hpp | 26 +- src/datastore_ls.cpp | 4 +- src/mpi_datastore_ls.cpp | 6 +- test/container/concurrent_map_test.cpp | 4 +- .../fallback_allocator_adaptor_test.cpp | 4 +- test/container/json/json_value.cpp | 24 +- test/container/stl_allocator_test.cpp | 50 +-- .../attributed_object_directory_test.cpp | 6 +- test/kernel/bin_directory_test.cpp | 6 +- test/kernel/chunk_directory_test.cpp | 6 +- test/kernel/manager_multithread_test.cpp | 6 +- test/kernel/manager_test.cpp | 302 +++++++++--------- test/kernel/multimanager_test.cpp | 16 +- .../kernel/object_attribute_accessor_test.cpp | 20 +- test/kernel/snapshot_test.cpp | 29 +- test/test_utility.hpp | 25 +- tutorial/hands_on/README.md | 15 +- tutorial/nvmw21/README.md | 15 +- verification/CMakeLists.txt | 3 +- verification/sparse_copy/CMakeLists.txt | 1 + .../sparse_copy/verify_sparse_copy.cpp | 87 +++++ 38 files changed, 625 insertions(+), 645 deletions(-) create mode 100644 cmake/check_cxx_filesystem_library.cmake delete mode 100644 cmake/include_cxx_filesystem_library.cmake create mode 100644 verification/sparse_copy/CMakeLists.txt create mode 100644 verification/sparse_copy/verify_sparse_copy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b818fa77..c0fb10f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,13 @@ include(FetchContent) # -------------------------------------------------------------------------------- # # CMake policy # -------------------------------------------------------------------------------- # -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") cmake_policy(SET CMP0077 NEW) -endif() +endif () -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") cmake_policy(SET CMP0135 NEW) -endif() +endif () # -------------------------------------------------------------------------------- # # Metall general configuration @@ -113,11 +113,11 @@ endif () # -------------------------------------------------------------------------------- # if (INSTALL_HEADER_ONLY) message(WARNING "INSTALL_HEADER_ONLY option has been replaced with JUST_INSTALL_METALL_HEADER.") -endif() +endif () if (JUST_INSTALL_METALL_HEADER) return() -endif() +endif () # -------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------- # @@ -138,7 +138,7 @@ endif () # -------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------- # -# Executables +# Set up for building executables # -------------------------------------------------------------------------------- # # Requirements for GCC @@ -151,9 +151,9 @@ if (NOT RUN_BUILD_AND_TEST_WITH_CI) endif () # ---------- Metall Macros ---------- # -foreach(X ${COMPILER_DEFS}) +foreach (X ${COMPILER_DEFS}) message(STATUS "Metall compile definition: ${X}") -endforeach() +endforeach () # ---------- CMAKE_BUILD_TYPE ---------- # @@ -168,12 +168,8 @@ find_package(Threads REQUIRED) # ---------- filesystem ---------- # -include(include_cxx_filesystem_library) -include_cxx_filesystem_library() - -# Xcode 11 Beta Release Notes -# Clang now supports the C++17 library for iOS 13, macOS 10.15, watchOS 6, and tvOS 13. (50988273) -# https://developer.apple.com/documentation/xcode_release_notes/xcode_11_beta_release_notes?language=objc +include(check_cxx_filesystem_library) +check_cxx_filesystem_library() # ---------- UMap ---------- # @@ -192,7 +188,7 @@ set(Boost_NO_BOOST_CMAKE ON) find_package(Boost 1.64 QUIET) if (NOT Boost_FOUND) FetchContent_Declare(Boost - URL https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2) + URL https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2) FetchContent_GetProperties(Boost) if (NOT Boost_POPULATED) FetchContent_Populate(Boost) @@ -246,20 +242,18 @@ function(common_setup_for_metall_executable name) # -------------------- # ----- Compile Definitions ----- # - foreach(X ${COMPILER_DEFS}) - target_compile_definitions(${name} PRIVATE ${X}) - endforeach() + foreach (X ${COMPILER_DEFS}) + target_compile_definitions(${name} PRIVATE ${X}) + endforeach () # -------------------- # ----- CXX17 Filesystem Lib----- # - # include_cxx_filesystem_library module must be executed first - if (FOUND_CXX17_FILESYSTEM_LIB) - if (REQUIRE_LIB_STDCXX_FS) + # GNU compilers prior to 9.1 requires linking with stdc++fs + if (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) target_link_libraries(${name} PRIVATE stdc++fs) - endif() - elseif() - target_compile_definitions(${name} PRIVATE "METALL_DISABLE_CXX17_FILESYSTEM_LIB") - endif() + endif () + endif () # -------------------- # ----- Umap----- # diff --git a/bench/bfs/run_bfs_bench_metall.cpp b/bench/bfs/run_bfs_bench_metall.cpp index ccf3b0b4..a4587060 100644 --- a/bench/bfs/run_bfs_bench_metall.cpp +++ b/bench/bfs/run_bfs_bench_metall.cpp @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { // metall::logger::set_log_level(metall::logger::level::verbose); metall::manager manager(metall::open_read_only, - option.graph_file_name_list[0].c_str()); + option.graph_file_name_list[0]); auto adj_list = manager.find(option.graph_key_name.c_str()).first; diff --git a/bench/bfs/run_bfs_bench_metall_multiple.cpp b/bench/bfs/run_bfs_bench_metall_multiple.cpp index 6fa7e2d3..fd27f6a2 100644 --- a/bench/bfs/run_bfs_bench_metall_multiple.cpp +++ b/bench/bfs/run_bfs_bench_metall_multiple.cpp @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { std::vector managers; for (const auto &file_name : option.graph_file_name_list) { managers.emplace_back( - new metall::manager(metall::open_read_only, file_name.c_str())); + new metall::manager(metall::open_read_only, file_name)); } auto adj_list = adjacency_list_type(option.graph_key_name, managers.begin(), diff --git a/bench/simple_alloc/run_simple_allocation_bench_metall.cpp b/bench/simple_alloc/run_simple_allocation_bench_metall.cpp index 1b9d5249..ff3c0246 100644 --- a/bench/simple_alloc/run_simple_allocation_bench_metall.cpp +++ b/bench/simple_alloc/run_simple_allocation_bench_metall.cpp @@ -13,7 +13,7 @@ int main(int argc, char *argv[]) { const auto option = simple_alloc_bench::parse_option(argc, argv); { - metall::manager manager(metall::create_only, option.datastore_path.c_str()); + metall::manager manager(metall::create_only, option.datastore_path); simple_alloc_bench::run_bench(option, manager.get_allocator()); } metall::manager::remove(option.datastore_path.c_str()); diff --git a/cmake/check_cxx_filesystem_library.cmake b/cmake/check_cxx_filesystem_library.cmake new file mode 100644 index 00000000..88eb5ad7 --- /dev/null +++ b/cmake/check_cxx_filesystem_library.cmake @@ -0,0 +1,25 @@ +# Checks if the C++17 library is available. +function(check_cxx_filesystem_library) + + set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) + set(REQUIRE_LIB_STDCXX_FS FALSE PARENT_SCOPE) + + # Check if C++17 header files are available + # If not, uses our own implementation. + include(CheckIncludeFileCXX) + CHECK_INCLUDE_FILE_CXX(filesystem FOUND_FILESYSTEM_HEADER) + if (NOT FOUND_FILESYSTEM_HEADER) + message(FATAL_ERROR "Cannot find the C++17 library.") + endif () + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang + if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") # macOS + include(get_macos_version) + get_macos_version() # Get macOS version + message(VERBOSE "Detected macOS version ${MACOS_VERSION}") + if (MACOS_VERSION VERSION_LESS 10.15) # macOS < 10.15 + message(FATAL_ERROR "macOS >= 10.15 is required to use the C++17 library.") + endif () + endif () + endif () +endfunction() \ No newline at end of file diff --git a/cmake/include_cxx_filesystem_library.cmake b/cmake/include_cxx_filesystem_library.cmake deleted file mode 100644 index a54ca061..00000000 --- a/cmake/include_cxx_filesystem_library.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# Find the correct option to link the C++17 library. -# If it is not available, uses own implementation. -function(include_cxx_filesystem_library) - - set(FOUND_CXX17_FILESYSTEM_LIB TRUE PARENT_SCOPE) - set(REQUIRE_LIB_STDCXX_FS FALSE PARENT_SCOPE) - - # Check if C++17 header files are available - # If not, uses our own implementation. - include(CheckIncludeFileCXX) - CHECK_INCLUDE_FILE_CXX(filesystem FOUND_FILESYSTEM_HEADER) - if (NOT FOUND_FILESYSTEM_HEADER) - message(STATUS "Cannot find the C++17 library.") - set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) - return() - endif () - - # Find the correct option to link the C++17 library - # GCC - # Any platform: stdc++fs - # LLVM - # Assumes LLVM >= 9.0 and libc++ >= 7.0. - # Clang + macOS >= 10.15: nothing special is required to use - # Clang + macOS < 10.15: uses our own implementation - # Clang + the other OS: nothing special is required to use - if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")) # GCC - set(REQUIRE_LIB_STDCXX_FS TRUE PARENT_SCOPE) - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang - if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") # macOS - include(get_macos_version) - get_macos_version() # Get macOS version - message(VERBOSE "macOS version ${MACOS_VERSION}") - if (MACOS_VERSION VERSION_LESS 10.15) # macOS < 10.15 - message(STATUS "macOS >= 10.15 is required to use the C++17 library.") - set(FOUND_CXX17_FILESYSTEM_LIB FALSE PARENT_SCOPE) - endif () - endif () - endif () - -endfunction() \ No newline at end of file diff --git a/docs/readthedocs/basics/getting_started.md b/docs/readthedocs/basics/getting_started.md index 45965228..79c8a629 100644 --- a/docs/readthedocs/basics/getting_started.md +++ b/docs/readthedocs/basics/getting_started.md @@ -69,22 +69,11 @@ g++ -std=c++17 your_program.cpp -lstdc++fs -I${BOOST_ROOT}/include -I${METALL_RO Clang (or Apple clang) could be used instead of GCC to build Metall. However, we haven't tested it intensively. +To run on macOS, Metall requires macOS >= 10.15. + Also, Boost C++ Libraries 1.69 or more may be required if one wants to build Metall with Clang + CUDA. -**On macOS >= 10.15 or Linux** - ```bash -# Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB -``` +``` \ No newline at end of file diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index fb18ca9f..5487bbed 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -7,7 +7,9 @@ #define METALL_BASIC_MANAGER_HPP #include +#include #include +#include #include #include @@ -116,7 +118,7 @@ class basic_manager { /// \brief Opens an existing data store. /// \param base_path Path to a data store. - basic_manager(open_only_t, const char *base_path) noexcept { + basic_manager(open_only_t, const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open(base_path); @@ -130,7 +132,8 @@ class basic_manager { /// \brief Opens an existing data store with the read only mode. /// Write accesses will cause segmentation fault. /// \param base_path Path to a data store. - basic_manager(open_read_only_t, const char *base_path) noexcept { + basic_manager(open_read_only_t, + const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->open_read_only(base_path); @@ -143,7 +146,8 @@ class basic_manager { /// \brief Creates a new data store (an existing data store will be /// overwritten). \param base_path Path to create a data store. - basic_manager(create_only_t, const char *base_path) noexcept { + basic_manager(create_only_t, + const std::filesystem::path &base_path) noexcept { try { m_kernel = std::make_unique(); m_kernel->create(base_path); @@ -157,7 +161,7 @@ class basic_manager { /// \brief Creates a new data store (an existing data store will be /// overwritten). \param base_path Path to create a data store. \param /// capacity Maximum total allocation size. - basic_manager(create_only_t, const char *base_path, + basic_manager(create_only_t, const std::filesystem::path &base_path, const size_type capacity) noexcept { try { m_kernel = std::make_unique(); @@ -940,7 +944,8 @@ class basic_manager { /// if it is available. \param num_max_copy_threads The maximum number of copy /// threads to use. If <= 0 is given, the value is automatically determined. /// \return Returns true on success; other false. - bool snapshot(const char_type *destination_dir_path, const bool clone = true, + bool snapshot(const std::filesystem::path &destination_dir_path, + const bool clone = true, const int num_max_copy_threads = 0) noexcept { if (!check_sanity()) { return false; @@ -968,8 +973,8 @@ class basic_manager { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns true; other false. - static bool copy(const char_type *source_dir_path, - const char_type *destination_dir_path, + static bool copy(const std::filesystem::path &source_dir_path, + const std::filesystem::path &destination_dir_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { @@ -995,8 +1000,8 @@ class basic_manager { /// If <= 0 is given, the value is automatically determined. /// \return Returns an object of std::future. /// If succeeded, its get() returns true; other false. - static auto copy_async(const char_type *source_dir_path, - const char_type *destination_dir_path, + static auto copy_async(const std::filesystem::path &source_dir_path, + const std::filesystem::path &destination_dir_path, const bool clone = true, const int num_max_copy_threads = 0) noexcept { try { @@ -1015,7 +1020,7 @@ class basic_manager { /// /// \param dir_path Path to a data store to remove. \return If /// succeeded, returns true; other false. - static bool remove(const char_type *dir_path) noexcept { + static bool remove(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::remove(dir_path); } catch (...) { @@ -1032,7 +1037,8 @@ class basic_manager { /// \param dir_path Path to a data store to remove. /// \return Returns an object of std::future. /// If succeeded, its get() returns true; other false - static std::future remove_async(const char_type *dir_path) noexcept { + static std::future remove_async( + const std::filesystem::path &dir_path) noexcept { try { return std::async(std::launch::async, remove, dir_path); } catch (...) { @@ -1054,7 +1060,7 @@ class basic_manager { /// \param dir_path Path to a data store. /// \return Returns true if it exists and is consistent; otherwise, returns /// false. - static bool consistent(const char_type *dir_path) noexcept { + static bool consistent(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::consistent(dir_path); } catch (...) { @@ -1086,7 +1092,7 @@ class basic_manager { /// /// \param dir_path Path to a data store. /// \return UUID in the std::string format; returns an empty string on error. - static std::string get_uuid(const char_type *dir_path) noexcept { + static std::string get_uuid(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::get_uuid(dir_path); } catch (...) { @@ -1118,7 +1124,7 @@ class basic_manager { /// /// \param dir_path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char_type *dir_path) noexcept { + static version_type get_version(const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::get_version(dir_path); } catch (...) { @@ -1161,7 +1167,7 @@ class basic_manager { /// \param dir_path Path to a data store. \param description An std::string /// object that holds a description. \return Returns true on success; /// otherwise, false. - static bool set_description(const char *dir_path, + static bool set_description(const std::filesystem::path &dir_path, const std::string &description) noexcept { try { return manager_kernel_type::set_description(dir_path, description); @@ -1203,7 +1209,7 @@ class basic_manager { /// to an std::string object to store a description if it exists. \return /// Returns true on success; returns false on error. Trying to get a /// non-existent description is not considered as an error. - static bool get_description(const char *dir_path, + static bool get_description(const std::filesystem::path &dir_path, std::string *description) noexcept { try { return manager_kernel_type::get_description(dir_path, description); @@ -1222,7 +1228,7 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an instance /// of named_object_attribute_accessor_type. static named_object_attribute_accessor_type access_named_object_attribute( - const char *dir_path) noexcept { + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_named_object_attribute(dir_path); } catch (...) { @@ -1239,7 +1245,7 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an instance /// of unique_object_attribute_accessor_type. static unique_object_attribute_accessor_type access_unique_object_attribute( - const char *dir_path) noexcept { + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_unique_object_attribute(dir_path); } catch (...) { @@ -1256,7 +1262,8 @@ class basic_manager { /// \param dir_path Path to a data store. \return Returns an /// instance of anonymous_object_attribute_accessor_type. static anonymous_object_attribute_accessor_type - access_anonymous_object_attribute(const char *dir_path) noexcept { + access_anonymous_object_attribute( + const std::filesystem::path &dir_path) noexcept { try { return manager_kernel_type::access_anonymous_object_attribute(dir_path); } catch (...) { diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index f10a999e..88014901 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -28,37 +28,15 @@ #include #include #include - -#ifdef __has_include - -// Check if the Filesystem library is available or disabled by the user -#if __has_include() && !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) #include -#else -#ifdef METALL_VERBOSE_SYSTEM_SUPPORT_WARNING -#warning "The Filesystem library is not available or disabled by the user." -#endif -#endif - -#else // __has_include is not defined - -#ifdef METALL_VERBOSE_SYSTEM_SUPPORT_WARNING -#warning \ - "__has_include is not defined, consequently disable the Filesystem library." -#endif // METALL_VERBOSE_SYSTEM_SUPPORT_WARNING - -#endif // #ifdef __has_include #include namespace metall::mtlldetail { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) namespace { namespace fs = std::filesystem; } -#endif inline bool os_close(const int fd) { if (::close(fd) == -1) { @@ -76,7 +54,7 @@ inline bool os_fsync(const int fd) { return true; } -inline bool fsync(const std::string &path) { +inline bool fsync(const fs::path &path) { const int fd = ::open(path.c_str(), O_RDONLY); if (fd == -1) { logger::perror(logger::level::error, __FILE__, __LINE__, "open"); @@ -90,9 +68,7 @@ inline bool fsync(const std::string &path) { return ret; } -inline bool fsync_recursive(const std::string &path) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) +inline bool fsync_recursive(const fs::path &path) { fs::path p(path); p = fs::canonical(p); while (true) { @@ -105,23 +81,6 @@ inline bool fsync_recursive(const std::string &path) { p = p.parent_path(); } return true; -#else - char *abs = ::realpath(path.c_str(), NULL); - if (!abs) return false; - char *ref = abs; - while (true) { - if (!fsync(std::string(abs))) { - ::free(ref); - return false; - } - if (::strcmp(abs, "/") == 0) { - break; - } - abs = ::dirname(abs); - } - ::free(ref); - return true; -#endif } inline bool extend_file_size_manually(const int fd, const off_t offset, @@ -130,7 +89,7 @@ inline bool extend_file_size_manually(const int fd, const off_t offset, for (off_t i = offset; i < file_size / 4096 + offset; ++i) { ::pwrite(fd, buffer, 4096, i * 4096); } - const size_t remained_size = file_size % 4096; + const std::size_t remained_size = file_size % 4096; if (remained_size > 0) ::pwrite(fd, buffer, remained_size, file_size - remained_size); @@ -141,7 +100,7 @@ inline bool extend_file_size_manually(const int fd, const off_t offset, return ret; } -inline bool extend_file_size(const int fd, const size_t file_size, +inline bool extend_file_size(const int fd, const std::size_t file_size, const bool fill_with_zero) { if (fill_with_zero) { #ifdef __APPLE__ @@ -157,8 +116,7 @@ inline bool extend_file_size(const int fd, const size_t file_size, } #endif } else { - // ----- extend the file if its size is smaller than that of mapped area - // ----- // + // extend the file if its size is smaller than that of mapped area struct stat stat_buf; if (::fstat(fd, &stat_buf) == -1) { logger::perror(logger::level::error, __FILE__, __LINE__, "fstat"); @@ -176,8 +134,8 @@ inline bool extend_file_size(const int fd, const size_t file_size, return ret; } -inline bool extend_file_size(const std::string &file_path, - const size_t file_size, +inline bool extend_file_size(const fs::path &file_path, + const std::size_t file_size, const bool fill_with_zero = false) { const int fd = ::open(file_path.c_str(), O_RDWR); if (fd == -1) { @@ -193,22 +151,23 @@ inline bool extend_file_size(const std::string &file_path, /// \brief Check if a file, any kinds of file including directory, exists /// \warning This implementation could return a wrong result due to metadata -/// cache on NFS. The following code could fail: if (mpi_rank == 1) -/// file_exist(path); // NFS creates metadata cache mpi_barrier(); if (mpi_rank -/// == 0) create_directory(path); mpi_barrier(); if (mpi_rank == 1) +/// cache on NFS. The following code could fail: +/// if (mpi_rank == 1) +/// file_exist(path); // NFS creates metadata cache +/// mpi_barrier(); +/// if (mpi_rank == 0) +/// create_directory(path); +/// mpi_barrier(); +/// if (mpi_rank == 1) /// assert(file_exist(path)); // Could fail due to the cached metadata. -inline bool file_exist(const std::string &file_name) { - std::string fixed_string(file_name); - while (fixed_string.back() == '/') { - fixed_string.pop_back(); - } - return (::access(fixed_string.c_str(), F_OK) == 0); +inline bool file_exist(const fs::path &file_name) { + return fs::exists(file_name); } /// \brief Check if a directory exists /// \warning This implementation could return a wrong result due to metadata /// cache on NFS. -inline bool directory_exist(const std::string &dir_path) { +inline bool directory_exist(const fs::path &dir_path) { struct stat stat_buf; if (::stat(dir_path.c_str(), &stat_buf) == -1) { return false; @@ -216,7 +175,7 @@ inline bool directory_exist(const std::string &dir_path) { return S_ISDIR(stat_buf.st_mode); } -inline bool create_file(const std::string &file_path) { +inline bool create_file(const fs::path &file_path) { const int fd = ::open(file_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd == -1) { @@ -229,13 +188,11 @@ inline bool create_file(const std::string &file_path) { return fsync_recursive(file_path); } -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) /// \brief Creates directories recursively. /// \return Returns true if the directory was created or already exists. /// Otherwise, returns false. -inline bool create_directory(const std::string &dir_path) { - std::string fixed_string = dir_path; +inline bool create_directory(const fs::path &dir_path) { + fs::path fixed_string = dir_path; // MEMO: GCC bug 87846 (fixed in v8.3) // "Calling std::filesystem::create_directories with a path with a trailing // separator (e.g. "./a/b/") does not create any directory." @@ -269,18 +226,8 @@ inline bool create_directory(const std::string &dir_path) { return success; } -#else -/// \brief Creates directories recursively. -/// \return Returns true if the directory was created or already exists, returns -/// true. Otherwise, returns false. -inline bool create_directory(const std::string &dir_path) { - std::string mkdir_command("mkdir -p " + dir_path); - const int status = std::system(mkdir_command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); -} -#endif -inline ssize_t get_file_size(const std::string &file_path) { +inline ssize_t get_file_size(const fs::path &file_path) { std::ifstream ifs(file_path, std::ifstream::binary | std::ifstream::ate); ssize_t size = ifs.tellg(); if (size == -1) { @@ -295,31 +242,23 @@ inline ssize_t get_file_size(const std::string &file_path) { /// \brief /// Note that, according to GCC, /// the file system may use some blocks for internal record keeping -inline ssize_t get_actual_file_size(const std::string &file_path) { +inline ssize_t get_actual_file_size(const fs::path &file_path) { struct stat stat_buf; if (::stat(file_path.c_str(), &stat_buf) != 0) { - std::string s("stat (" + file_path + ")"); + std::string s("stat (" + file_path.string() + ")"); logger::perror(logger::level::error, __FILE__, __LINE__, s.c_str()); return -1; } - return stat_buf.st_blocks * 512LL; + return ssize_t(stat_buf.st_blocks) * ssize_t(stat_buf.st_blksize); } /// \brief Remove a file or directory /// \return Upon successful completion, returns true; otherwise, false is /// returned. If the file or directory does not exist, true is returned. -inline bool remove_file(const std::string &path) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) - std::filesystem::path p(path); +inline bool remove_file(const fs::path &path) { std::error_code ec; - [[maybe_unused]] const auto num_removed = std::filesystem::remove_all(p, ec); + std::filesystem::remove_all(path, ec); return !ec; -#else - std::string rm_command("rm -rf " + path); - const int status = std::system(rm_command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); -#endif } inline bool free_file_space([[maybe_unused]] const int fd, @@ -341,12 +280,10 @@ inline bool free_file_space([[maybe_unused]] const int fd, #endif } -namespace file_copy_detail { +namespace fcpdtl { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) -inline bool copy_file_dense(const std::string &source_path, - const std::string &destination_path) { +inline bool copy_file_dense(const fs::path &source_path, + const fs::path &destination_path) { bool success = true; try { if (!fs::copy_file(source_path, destination_path, @@ -369,77 +306,79 @@ inline bool copy_file_dense(const std::string &source_path, return success; } -#else - -inline bool copy_file_dense(const std::string &source_path, - const std::string &destination_path) { - { - const ssize_t source_file_size = get_file_size(source_path); - const ssize_t actual_source_file_size = get_actual_file_size(source_path); - if (source_file_size == -1 || actual_source_file_size == -1) { - return false; - } - - // If the source file is empty, just create the destination file and done. - if (source_file_size == 0 || actual_source_file_size == 0) { - create_file(destination_path); - return true; - } +inline bool copy_file_sparse_manually(const fs::path &source_path, + const fs::path &destination_path) { + const auto src_size = get_file_size(source_path); + if (src_size == -1) { + return false; + } + if (!extend_file_size(destination_path, src_size, false)) { + return false; } - { - std::ifstream source(source_path); - if (!source.is_open()) { + std::ifstream source; + std::ofstream dest; + const auto open_file = [](const auto &path, auto &file) { + file.open(path, std::ios::binary); + if (!file.is_open()) { std::stringstream ss; - ss << "Cannot open: " << source_path; + ss << "Failed to open file: " << path; logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); return false; } + return true; + }; - std::ofstream destination(destination_path); - if (!destination.is_open()) { - std::stringstream ss; - ss << "Cannot open: " << destination_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } + if (!open_file(source_path, source) || !open_file(destination_path, dest)) { + return false; + } - destination << source.rdbuf(); - if (!destination) { - std::stringstream ss; - ss << "Something happened in the ofstream: " << destination_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } + const std::size_t block_size = 512; + char buffer[block_size]; - destination.close(); + const auto is_sparse = [](const char *const buffer, const std::size_t size) { + constexpr std::size_t chunk_size = sizeof(uint64_t); + const std::size_t num_chunks = size / chunk_size; + for (std::size_t i = 0; i < num_chunks; ++i) { + if (*reinterpret_cast(buffer + i * chunk_size) != + uint64_t(0)) { + return false; + } + } + // Check the remaining bytes + for (std::size_t i = num_chunks * chunk_size; i < size; ++i) { + if (buffer[i] != char(0)) { + return false; + } + } + return true; + }; - if (!metall::mtlldetail::fsync(destination_path)) { - return false; + while (source.read(buffer, block_size) || source.gcount() > 0) { + if (!is_sparse(buffer, source.gcount())) { + dest.write(buffer, source.gcount()); + } else { + dest.seekp(source.gcount(), std::ios_base::cur); } } - { - // Sanity check - const ssize_t s1 = get_file_size(source_path); - const ssize_t s2 = get_file_size(destination_path); - if (s1 < 0 || s1 != s2) { - std::stringstream ss; - ss << "Something wrong in file sizes: " << s1 << " " << s2; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); - return false; - } + source.close(); + dest.close(); + if (!dest) { + std::stringstream ss; + ss << "Failed to write file: " << destination_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return false; } + return true; } -#endif - #ifdef __linux__ -inline bool copy_file_sparse_linux(const std::string &source_path, - const std::string &destination_path) { - std::string command("cp --sparse=auto " + source_path + " " + - destination_path); +inline bool copy_file_sparse_linux(const fs::path &source_path, + const fs::path &destination_path) { + std::string command("cp --sparse=auto " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); const bool success = (status != -1) && !!(WIFEXITED(status)); if (!success) { @@ -452,26 +391,25 @@ inline bool copy_file_sparse_linux(const std::string &source_path, } #endif -} // namespace file_copy_detail +} // namespace fcpdtl /// \brief Copy a file. /// \param source_path A source file path. /// \param destination_path A destination path. /// \param sparse_copy If true is specified, tries to perform sparse file copy. /// \return On success, returns true. On error, returns false. -inline bool copy_file(const std::string &source_path, - const std::string &destination_path, +inline bool copy_file(const fs::path &source_path, + const fs::path &destination_path, const bool sparse_copy = true) { if (sparse_copy) { #ifdef __linux__ - return file_copy_detail::copy_file_sparse_linux(source_path, - destination_path); + return fcpdtl::copy_file_sparse_linux(source_path, destination_path); #else logger::out(logger::level::info, __FILE__, __LINE__, "Sparse file copy is not available"); #endif } - return file_copy_detail::copy_file_dense(source_path, destination_path); + return fcpdtl::copy_file_dense(source_path, destination_path); } /// \brief Get the file names in a directory. @@ -481,11 +419,8 @@ inline bool copy_file(const std::string &source_path, /// \param file_list A buffer to put results. /// \return Returns true if there is no error (empty directory returns true as /// long as the operation does not fail). Returns false on error. -inline bool get_regular_file_names(const std::string &dir_path, - std::vector *file_list) { -#if defined(__cpp_lib_filesystem) && \ - !defined(METALL_DISABLE_CXX17_FILESYSTEM_LIB) - +inline bool get_regular_file_names(const fs::path &dir_path, + std::vector *file_list) { if (!directory_exist(dir_path)) { return false; } @@ -504,22 +439,6 @@ inline bool get_regular_file_names(const std::string &dir_path, } return true; - -#else - DIR *d = ::opendir(dir_path.c_str()); - if (!d) { - return false; - } - - for (dirent *dir; (dir = ::readdir(d)) != nullptr;) { - if (dir->d_type == DT_REG) { - file_list->push_back(dir->d_name); - } - } - ::closedir(d); - - return true; -#endif } /// \brief Copy files in a directory. @@ -532,11 +451,10 @@ inline bool get_regular_file_names(const std::string &dir_path, /// \param copy_func The actual copy function. /// \return On success, returns true. On error, returns false. inline bool copy_files_in_directory_in_parallel_helper( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads, - const std::function - ©_func) { - std::vector src_file_names; + const std::function ©_func) { + std::vector src_file_names; if (!get_regular_file_names(source_dir_path, &src_file_names)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to get file list"); @@ -550,10 +468,9 @@ inline bool copy_files_in_directory_in_parallel_helper( while (true) { const auto file_no = file_no_cnt.fetch_add(1); if (file_no >= src_file_names.size()) break; - const std::string &src_file_path = - source_dir_path + "/" + src_file_names[file_no]; - const std::string &dst_file_path = - destination_dir_path + "/" + src_file_names[file_no]; + const fs::path &src_file_path = source_dir_path / src_file_names[file_no]; + const fs::path &dst_file_path = + destination_dir_path / src_file_names[file_no]; num_successes.fetch_add(copy_func(src_file_path, dst_file_path) ? 1 : 0); } }; @@ -583,11 +500,11 @@ inline bool copy_files_in_directory_in_parallel_helper( /// \param sparse_copy Performs sparse file copy. /// \return On success, returns true. On error, returns false. inline bool copy_files_in_directory_in_parallel( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads, const bool sparse_copy = true) { return copy_files_in_directory_in_parallel_helper( source_dir_path, destination_dir_path, max_num_threads, - [&sparse_copy](const std::string &src, const std::string &dst) -> bool { + [&sparse_copy](const fs::path &src, const fs::path &dst) -> bool { return copy_file(src, dst, sparse_copy); }); } diff --git a/include/metall/detail/file_clone.hpp b/include/metall/detail/file_clone.hpp index f4cdf8df..81d3d071 100644 --- a/include/metall/detail/file_clone.hpp +++ b/include/metall/detail/file_clone.hpp @@ -23,21 +23,26 @@ namespace metall::mtlldetail { +namespace { +namespace fs = std::filesystem; +} + namespace file_clone_detail { #ifdef __linux__ -inline bool clone_file_linux(const std::string &source_path, - const std::string &destination_path) { - std::string command("cp --reflink=auto -R " + source_path + " " + - destination_path); +inline bool clone_file_linux(const fs::path &source_path, + const fs::path &destination_path) { + std::string command("cp --reflink=auto -R " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); return (status != -1) && !!(WIFEXITED(status)); } #endif #ifdef __APPLE__ -inline bool clone_file_macos(const std::string &source_path, - const std::string &destination_path) { - std::string command("cp -cR " + source_path + " " + destination_path); +inline bool clone_file_macos(const fs::path &source_path, + const fs::path &destination_path) { + std::string command("cp -cR " + source_path.string() + " " + + destination_path.string()); const int status = std::system(command.c_str()); return (status != -1) && !!(WIFEXITED(status)); } @@ -48,21 +53,21 @@ inline bool clone_file_macos(const std::string &source_path, /// normally. \param source_path A path to the file to be cloned. \param /// destination_path A path to copy to. \return On success, returns true. On /// error, returns false. -inline bool clone_file(const std::string &source_path, - const std::string &destination_path) { +inline bool clone_file(const fs::path &source_path, + const fs::path &destination_path) { bool ret = false; #if defined(__linux__) ret = file_clone_detail::clone_file_linux(source_path, destination_path); if (!ret) { - std::string s("On Linux, Failed to clone " + source_path + " to " + - destination_path); + std::string s("On Linux, Failed to clone " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #elif defined(__APPLE__) ret = file_clone_detail::clone_file_macos(source_path, destination_path); if (!ret) { - std::string s("On MacOS, Failed to clone " + source_path + " to " + - destination_path); + std::string s("On MacOS, Failed to clone " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #else @@ -73,7 +78,8 @@ inline bool clone_file(const std::string &source_path, "Use normal copy instead of clone"); ret = copy_file(source_path, destination_path); // Copy normally if (!ret) { - std::string s("Failed to copy " + source_path + " to " + destination_path); + std::string s("Failed to copy " + source_path.string() + " to " + + destination_path.string()); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); } #endif @@ -93,7 +99,7 @@ inline bool clone_file(const std::string &source_path, /// If <= 0 is given, it is automatically determined. /// \return On success, returns true. On error, returns false. inline bool clone_files_in_directory_in_parallel( - const std::string &source_dir_path, const std::string &destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const int max_num_threads) { return copy_files_in_directory_in_parallel_helper( source_dir_path, destination_dir_path, max_num_threads, clone_file); diff --git a/include/metall/detail/mmap.hpp b/include/metall/detail/mmap.hpp index 8f14e7ed..39dac965 100644 --- a/include/metall/detail/mmap.hpp +++ b/include/metall/detail/mmap.hpp @@ -98,7 +98,7 @@ inline void *map_anonymous_write_mode(void *const addr, const size_t length, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_read_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDONLY); @@ -147,7 +147,7 @@ inline void *map_file_write_mode(const int fd, void *const addr, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_write_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDWR); @@ -197,7 +197,7 @@ inline void *map_file_write_private_mode(const int fd, void *const addr, /// \return A pair of the file descriptor of the file and the starting address /// for the map inline std::pair map_file_write_private_mode( - const std::string &file_name, void *const addr, const size_t length, + const fs::path &file_name, void *const addr, const size_t length, const off_t offset, const int additional_flags = 0) { // ----- Open the file ----- // const int fd = ::open(file_name.c_str(), O_RDWR); diff --git a/include/metall/detail/ptree.hpp b/include/metall/detail/ptree.hpp index e547fb7c..13ef5e74 100644 --- a/include/metall/detail/ptree.hpp +++ b/include/metall/detail/ptree.hpp @@ -6,6 +6,9 @@ #ifndef METALL_DETAIL_UTILITY_PTREE_HPP #define METALL_DETAIL_UTILITY_PTREE_HPP +#include +#include + #include #include @@ -14,6 +17,7 @@ namespace metall::mtlldetail::ptree { namespace { +namespace fs = std::filesystem; namespace bptree = boost::property_tree; } @@ -117,7 +121,7 @@ inline bool push_back(const node_type &child, node_type *parent) { return true; } -inline bool read_json(const std::string &file_name, node_type *root) { +inline bool read_json(const fs::path &file_name, node_type *root) { try { bptree::read_json(file_name, *root); } catch (const bptree::json_parser_error &e) { @@ -127,7 +131,7 @@ inline bool read_json(const std::string &file_name, node_type *root) { return true; } -inline bool write_json(const node_type &root, const std::string &file_name) { +inline bool write_json(const node_type &root, const fs::path &file_name) { try { bptree::write_json(file_name, root); } catch (const bptree::json_parser_error &e) { diff --git a/include/metall/kernel/attributed_object_directory.hpp b/include/metall/kernel/attributed_object_directory.hpp index 3d85d5b4..83075b75 100644 --- a/include/metall/kernel/attributed_object_directory.hpp +++ b/include/metall/kernel/attributed_object_directory.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; namespace json = metall::mtlldetail::ptree; } // namespace @@ -429,7 +431,7 @@ class attributed_object_directory { /// \brief /// \param path - bool serialize(const char *const path) const noexcept { + bool serialize(const fs::path &path) const noexcept { try { return priv_serialize_throw(path); } catch (...) { @@ -440,7 +442,7 @@ class attributed_object_directory { /// \brief /// \param path - bool deserialize(const char *const path) noexcept { + bool deserialize(const fs::path &path) noexcept { try { return priv_deserialize_throw(path); } catch (...) { @@ -507,7 +509,7 @@ class attributed_object_directory { return true; } - bool priv_serialize_throw(const char *const path) const { + bool priv_serialize_throw(const fs::path &path) const { if (!good()) { return false; } @@ -546,7 +548,7 @@ class attributed_object_directory { return true; } - bool priv_deserialize_throw(const char *const path) { + bool priv_deserialize_throw(const fs::path &path) { if (!good()) { return false; } diff --git a/include/metall/kernel/bin_directory.hpp b/include/metall/kernel/bin_directory.hpp index e3b8bb29..f96f2d58 100644 --- a/include/metall/kernel/bin_directory.hpp +++ b/include/metall/kernel/bin_directory.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,7 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; } @@ -195,7 +197,7 @@ class bin_directory { /// \brief /// \param path - bool serialize(const char *path) const { + bool serialize(const fs::path &path) const { std::ofstream ofs(path); if (!ofs.is_open()) { std::stringstream ss; @@ -224,7 +226,7 @@ class bin_directory { /// \brief /// \param path - bool deserialize(const char *path) { + bool deserialize(const fs::path &path) { std::ifstream ifs(path); if (!ifs.is_open()) { std::stringstream ss; diff --git a/include/metall/kernel/chunk_directory.hpp b/include/metall/kernel/chunk_directory.hpp index d5174232..0cc70e8e 100644 --- a/include/metall/kernel/chunk_directory.hpp +++ b/include/metall/kernel/chunk_directory.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,8 +23,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace /// \brief Chunk directory class. /// Chunk directory is a table that stores information about chunks. @@ -294,7 +296,7 @@ class chunk_directory { /// \brief /// \param path - bool serialize(const char *path) const { + bool serialize(const fs::path &path) const { std::ofstream ofs(path); if (!ofs.is_open()) { std::stringstream ss; @@ -358,7 +360,7 @@ class chunk_directory { /// \brief /// \param path /// \return - bool deserialize(const char *path) { + bool deserialize(const fs::path &path) { std::ifstream ifs(path); if (!ifs.is_open()) { std::stringstream ss; diff --git a/include/metall/kernel/manager_kernel.hpp b/include/metall/kernel/manager_kernel.hpp index c7eb3614..3280b9e1 100644 --- a/include/metall/kernel/manager_kernel.hpp +++ b/include/metall/kernel/manager_kernel.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -53,8 +54,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace template class manager_kernel { @@ -102,8 +104,7 @@ class manager_kernel { #ifndef METALL_SEGMENT_BLOCK_SIZE #error "METALL_SEGMENT_BLOCK_SIZE is not defined." #endif - static constexpr size_type k_initial_segment_size = - METALL_SEGMENT_BLOCK_SIZE; + static constexpr size_type k_initial_segment_size = METALL_SEGMENT_BLOCK_SIZE; static_assert(k_initial_segment_size <= k_default_vm_reserve_size, "k_initial_segment_size must be <= k_default_vm_reserve_size"); static_assert(k_chunk_size <= k_initial_segment_size, @@ -195,7 +196,7 @@ class manager_kernel { /// \param base_dir_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool create(const char *base_dir_path, + bool create(const fs::path &base_dir_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore @@ -203,14 +204,14 @@ class manager_kernel { /// \param base_dir_path /// \param vm_reserve_size /// \return Returns true if success; otherwise, returns false - bool open(const char *base_dir_path, + bool open(const fs::path &base_dir_path, size_type vm_reserve_size = k_default_vm_reserve_size); /// \brief Opens an existing datastore with read only /// Expect to be called by a single thread /// \param base_dir_path /// \return Returns true if success; otherwise, returns false - bool open_read_only(const char *base_dir_path); + bool open_read_only(const fs::path &base_dir_path); /// \brief Expect to be called by a single thread void close(); @@ -372,7 +373,7 @@ class manager_kernel { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false - bool snapshot(const char *destination_dir_path, const bool clone, + bool snapshot(const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Copies a data store synchronously, keeping the same UUID. @@ -382,8 +383,8 @@ class manager_kernel { /// \param num_max_copy_threads The maximum number of copy threads to use. /// If <= 0 is given, the value is automatically determined. /// \return If succeeded, returns True; other false. - static bool copy(const char *source_dir_path, - const char *destination_dir_path, const bool clone, + static bool copy(const fs::path &source_dir_path, + const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Copies a data store asynchronously, keeping the same UUID. @@ -394,27 +395,27 @@ class manager_kernel { /// If <= 0 is given, the value is automatically determined. /// \return Returns an object of std::future. /// If succeeded, its get() returns True; other false. - static std::future copy_async(const char *source_dir_path, - const char *destination_dir_path, + static std::future copy_async(const fs::path &source_dir_path, + const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads); /// \brief Remove a data store synchronously /// \param base_dir_path /// \return If succeeded, returns True; other false - static bool remove(const char *base_dir_path); + static bool remove(const fs::path &base_dir_path); /// \brief Remove a data store asynchronously /// \param base_dir_path /// \return Returns an object of std::future /// If succeeded, its get() returns True; other false - static std::future remove_async(const char *base_dir_path); + static std::future remove_async(const fs::path &base_dir_path); /// \brief Check if the backing data store is consistent, /// i.e. it was closed properly. /// \param dir_path /// \return Return true if it is consistent; otherwise, returns false. - static bool consistent(const char *dir_path); + static bool consistent(const fs::path &dir_path); /// \brief Returns the UUID of the backing data store. /// \return Returns UUID in std::string; returns an empty string on error. @@ -423,7 +424,7 @@ class manager_kernel { /// \brief Returns the UUID of the backing data store. /// \param dir_path Path to a data store. /// \return Returns UUID in std::string; returns an empty string on error. - static std::string get_uuid(const char *dir_path); + static std::string get_uuid(const fs::path &dir_path); /// \brief Gets the version number of the backing data store. /// \return Returns a version number; returns 0 on error. @@ -432,7 +433,7 @@ class manager_kernel { /// \brief Gets the version number of the backing data store. /// \param dir_path Path to a data store. /// \return Returns a version number; returns 0 on error. - static version_type get_version(const char *dir_path); + static version_type get_version(const fs::path &dir_path); /// \brief Gets a description from a file. /// \param description A pointer to a string buffer. @@ -443,7 +444,7 @@ class manager_kernel { /// \param base_dir_path Path to a data store. /// \param description A pointer to a string buffer. /// \return Returns false on error. - static bool get_description(const std::string &base_dir_path, + static bool get_description(const fs::path &base_dir_path, std::string *description); /// \brief Sets a description to a file. @@ -550,9 +551,9 @@ class manager_kernel { bool priv_allocate_segment_header(void *addr); bool priv_deallocate_segment_header(); - bool priv_open(const char *base_dir_path, bool read_only, + bool priv_open(const fs::path &base_dir_path, bool read_only, size_type vm_reserve_size_request = 0); - bool priv_create(const char *base_dir_path, size_type vm_reserve_size); + bool priv_create(const fs::path &base_dir_path, size_type vm_reserve_size); // ---------- For serializing/deserializing ---------- // bool priv_serialize_management_data(); @@ -560,8 +561,8 @@ class manager_kernel { // ---------- snapshot ---------- // /// \brief Takes a snapshot. The snapshot has a different UUID. - bool priv_snapshot(const char *destination_base_dir_path, const bool clone, - const int num_max_copy_threads); + bool priv_snapshot(const fs::path &destination_base_dir_path, + const bool clone, const int num_max_copy_threads); // ---------- File operations ---------- // /// \brief Copies all backing files using reflink if possible diff --git a/include/metall/kernel/manager_kernel_impl.ipp b/include/metall/kernel/manager_kernel_impl.ipp index 2d9f7641..6bcb20ee 100644 --- a/include/metall/kernel/manager_kernel_impl.ipp +++ b/include/metall/kernel/manager_kernel_impl.ipp @@ -39,20 +39,20 @@ manager_kernel::~manager_kernel() noexcept { // Public methods // -------------------- // template -bool manager_kernel::create(const char *base_dir_path, +bool manager_kernel::create(const fs::path &base_dir_path, const size_type vm_reserve_size) { return m_good = priv_create(base_dir_path, vm_reserve_size); } template bool manager_kernel::open_read_only( - const char *base_dir_path) { + const fs::path &base_dir_path) { return m_good = priv_open(base_dir_path, true, 0); } template bool manager_kernel::open( - const char *base_dir_path, const size_type vm_reserve_size_request) { + const fs::path &base_dir_path, const size_type vm_reserve_size_request) { return m_good = priv_open(base_dir_path, false, vm_reserve_size_request); } @@ -448,40 +448,41 @@ manager_kernel::get_segment_size() const { template bool manager_kernel::snapshot( - const char *destination_base_dir_path, const bool clone, + const fs::path &destination_base_dir_path, const bool clone, const int num_max_copy_threads) { return priv_snapshot(destination_base_dir_path, clone, num_max_copy_threads); } template bool manager_kernel::copy( - const char *source_base_dir_path, const char *destination_base_dir_path, - const bool clone, const int num_max_copy_threads) { + const fs::path &source_base_dir_path, + const fs::path &destination_base_dir_path, const bool clone, + const int num_max_copy_threads) { return priv_copy_data_store(source_base_dir_path, destination_base_dir_path, clone, num_max_copy_threads); } template std::future manager_kernel::copy_async( - const char *source_dir_path, const char *destination_dir_path, + const fs::path &source_dir_path, const fs::path &destination_dir_path, const bool clone, const int num_max_copy_threads) { return std::async(std::launch::async, copy, source_dir_path, destination_dir_path, clone, num_max_copy_threads); } template -bool manager_kernel::remove(const char *base_dir_path) { +bool manager_kernel::remove(const fs::path &base_dir_path) { return priv_remove_data_store(base_dir_path); } template std::future manager_kernel::remove_async( - const char *base_dir_path) { + const fs::path &base_dir_path) { return std::async(std::launch::async, remove, base_dir_path); } template -bool manager_kernel::consistent(const char *dir_path) { +bool manager_kernel::consistent(const fs::path &dir_path) { return priv_consistent(dir_path); } @@ -491,7 +492,8 @@ std::string manager_kernel::get_uuid() const { } template -std::string manager_kernel::get_uuid(const char *dir_path) { +std::string manager_kernel::get_uuid( + const fs::path &dir_path) { json_store meta_data; if (!priv_read_management_metadata(dir_path, &meta_data)) { std::stringstream ss; @@ -509,7 +511,7 @@ version_type manager_kernel::get_version() const { template version_type manager_kernel::get_version( - const char *dir_path) { + const fs::path &dir_path) { json_store meta_data; if (!priv_read_management_metadata(dir_path, &meta_data)) { std::stringstream ss; @@ -523,7 +525,7 @@ version_type manager_kernel::get_version( template bool manager_kernel::get_description( - const std::string &base_dir_path, std::string *description) { + const fs::path &base_dir_path, std::string *description) { return priv_read_description(base_dir_path, description); } @@ -633,7 +635,7 @@ bool manager_kernel::priv_init_datastore_directory( } // Remove existing directory to certainly create a new data store - if (!remove(base_dir_path.c_str())) { + if (!remove(base_dir_path)) { std::string s("Failed to remove a directory: " + base_dir_path); logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); return false; @@ -944,7 +946,7 @@ void manager_kernel::priv_destruct_and_free_memory( template bool manager_kernel::priv_open( - const char *base_dir_path, const bool read_only, + const fs::path &base_dir_path, const bool read_only, const size_type vm_reserve_size_request) { if (!priv_validate_runtime_configuration()) { return false; @@ -1021,7 +1023,7 @@ bool manager_kernel::priv_open( template bool manager_kernel::priv_create( - const char *base_dir_path, const size_type vm_reserve_size) { + const fs::path &base_dir_path, const size_type vm_reserve_size) { if (!priv_validate_runtime_configuration()) { return false; } @@ -1159,7 +1161,7 @@ bool manager_kernel::priv_deserialize_management_data() { // ---------- snapshot ---------- // template bool manager_kernel::priv_snapshot( - const char *destination_base_dir_path, const bool clone, + const fs::path &destination_base_dir_path, const bool clone, const int num_max_copy_threads) { priv_sanity_check(); m_segment_storage.sync(true); diff --git a/include/metall/kernel/segment_allocator.hpp b/include/metall/kernel/segment_allocator.hpp index 7e601cea..0e53f1c8 100644 --- a/include/metall/kernel/segment_allocator.hpp +++ b/include/metall/kernel/segment_allocator.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -40,8 +41,9 @@ namespace metall { namespace kernel { namespace { +namespace fs = std::filesystem; namespace mdtl = metall::mtlldetail; -} +} // namespace template +#include + #include int main(int argc, char *argv[]) { @@ -12,7 +14,7 @@ int main(int argc, char *argv[]) { std::abort(); } - const std::string datastore_path = argv[1]; + const std::filesystem::path datastore_path = argv[1]; metall::utility::ls_named_object(datastore_path); std::cout << std::endl; diff --git a/src/mpi_datastore_ls.cpp b/src/mpi_datastore_ls.cpp index 57b9dd98..9ff1a43a 100644 --- a/src/mpi_datastore_ls.cpp +++ b/src/mpi_datastore_ls.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -14,14 +16,14 @@ int main(int argc, char *argv[]) { std::abort(); } - const std::string datastore_path = argv[1]; + const std::filesystem::path datastore_path = argv[1]; const int mpi_rank = (argc < 3) ? 0 : std::stoi(argv[2]); const auto local_datastore_path = metall::utility::mpi_datastore::make_local_dir_path(datastore_path, mpi_rank); - if (!metall::manager::consistent(local_datastore_path.c_str())) { + if (!metall::manager::consistent(local_datastore_path)) { std::cerr << "Inconsistent datastore or invalid datastore path" << std::endl; std::abort(); diff --git a/test/container/concurrent_map_test.cpp b/test/container/concurrent_map_test.cpp index 16fc3943..a14d61a3 100644 --- a/test/container/concurrent_map_test.cpp +++ b/test/container/concurrent_map_test.cpp @@ -5,6 +5,8 @@ #include "gtest/gtest.h" +#include + #include #include #include @@ -130,7 +132,7 @@ TEST(ConcurrentMapTest, Persistence) { metall::container::concurrent_map, std::hash, allocator_type, 2>; - const std::string file_path(test_utility::make_test_path()); + const std::filesystem::path file_path(test_utility::make_test_path()); test_utility::create_test_dir(); metall::mtlldetail::remove_file(file_path); diff --git a/test/container/fallback_allocator_adaptor_test.cpp b/test/container/fallback_allocator_adaptor_test.cpp index 34d0e6b3..4a1443a9 100644 --- a/test/container/fallback_allocator_adaptor_test.cpp +++ b/test/container/fallback_allocator_adaptor_test.cpp @@ -199,7 +199,7 @@ TEST(FallbackAllocatorAdaptorTest, PersistentConstructFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.construct("int")(10); @@ -212,7 +212,7 @@ TEST(FallbackAllocatorAdaptorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); const auto ret1 = manager.find("int"); ASSERT_NE(ret1.first, nullptr); diff --git a/test/container/json/json_value.cpp b/test/container/json/json_value.cpp index d2d6c279..f04ab36b 100644 --- a/test/container/json/json_value.cpp +++ b/test/container/json/json_value.cpp @@ -365,10 +365,10 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::create_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); auto *jv_copy = manager_copy.construct("jv")(manager_copy.get_allocator()); @@ -380,7 +380,7 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::open_read_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); const auto *const jv_copy = manager_copy.find("jv").first; check_json_string(*jv_copy); } @@ -391,10 +391,10 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); metall::manager manager_copy( metall::create_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); auto json_src = mj::parse(json_string, manager_src.get_allocator()); // Construct a new one from another instance that was allocated by another // allocator @@ -406,7 +406,7 @@ TEST(JSONValueTest, CopyDifferentMetallAllocator) { { metall::manager manager_copy( metall::open_read_only, - (test_utility::make_test_path() + "_copy").c_str()); + (test_utility::make_test_path().string() + "_copy")); const auto *const jv_copy = manager_copy.find("jv").first; check_json_string(*jv_copy); } @@ -420,10 +420,10 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::create_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); auto *jv_move = manager_move.construct("jv")(manager_move.get_allocator()); @@ -434,7 +434,7 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::open_read_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); const auto *const jv_move = manager_move.find("jv").first; check_json_string(*jv_move); } @@ -445,10 +445,10 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_src( metall::create_only, - (test_utility::make_test_path() + "_src").c_str()); + (test_utility::make_test_path().string() + "_src")); metall::manager manager_move( metall::create_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); // Construct a new one from another instance that was allocated by another // allocator auto *jv_move = manager_move.construct("jv")( @@ -460,7 +460,7 @@ TEST(JSONValueTest, MoveDifferentMetallAllocator) { { metall::manager manager_move( metall::open_read_only, - (test_utility::make_test_path() + "_move").c_str()); + (test_utility::make_test_path().string() + "_move")); const auto *const jv_move = manager_move.find("jv").first; check_json_string(*jv_move); } diff --git a/test/container/stl_allocator_test.cpp b/test/container/stl_allocator_test.cpp index 7935186a..af2685a2 100644 --- a/test/container/stl_allocator_test.cpp +++ b/test/container/stl_allocator_test.cpp @@ -4,19 +4,25 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) #include "gtest/gtest.h" + #include #include +#include + #include #include #include #include #include "../test_utility.hpp" +namespace { +namespace fs = std::filesystem; + template using alloc_type = metall::manager::allocator_type; -const std::string &dir_path() { - const static std::string path(test_utility::make_test_path()); +const fs::path &dir_path() { + const static fs::path path(test_utility::make_test_path()); return path; } @@ -100,8 +106,7 @@ TEST(StlAllocatorTest, Types) { }; using alloc_t = alloc_type; - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 24UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 24UL); alloc_t alloc = manager.get_allocator(); { @@ -130,7 +135,7 @@ TEST(StlAllocatorTest, Types) { } TEST(StlAllocatorTest, Exception) { - metall::manager manager(metall::create_only, dir_path().c_str(), 1UL << 24UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 24UL); alloc_type allocator = manager.get_allocator(); @@ -142,16 +147,16 @@ TEST(StlAllocatorTest, Exception) { ASSERT_THROW({ allocator.allocate(1UL << 24UL); }, std::bad_alloc); - ASSERT_THROW({ allocator.allocate(allocator.max_size() + 1); }, - std::bad_array_new_length); + ASSERT_THROW( + { allocator.allocate(allocator.max_size() + 1); }, + std::bad_array_new_length); metall::logger::set_log_level(metall::logger::level::error); } TEST(StlAllocatorTest, Container) { { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); using element_type = std::pair; boost::interprocess::vector> vector( @@ -178,8 +183,7 @@ TEST(StlAllocatorTest, NestedContainer) { alloc_type>>>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); map_type map(manager.get_allocator<>()); for (uint64_t i = 0; i < 1024; ++i) { @@ -198,8 +202,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.construct("int")(10); ASSERT_EQ(*a, 10); @@ -211,7 +214,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); const auto ret1 = manager.find("int"); ASSERT_NE(ret1.first, nullptr); @@ -228,7 +231,7 @@ TEST(StlAllocatorTest, PersistentConstructFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); ASSERT_FALSE(manager.destroy("int")); @@ -243,8 +246,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { boost::interprocess::vector>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); int *a = manager.find_or_construct("int")(10); ASSERT_EQ(*a, 10); @@ -255,7 +257,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); int *a = manager.find_or_construct("int")(20); ASSERT_EQ(*a, 10); @@ -267,7 +269,7 @@ TEST(StlAllocatorTest, PersistentConstructOrFind) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); ASSERT_FALSE(manager.destroy("int")); @@ -289,8 +291,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { alloc_type>>>; { - metall::manager manager(metall::create_only, dir_path().c_str(), - 1UL << 27UL); + metall::manager manager(metall::create_only, dir_path(), 1UL << 27UL); map_type *map = manager.construct("map")(manager.get_allocator<>()); (*map)[0].emplace_back(1); @@ -298,7 +299,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { } { - metall::manager manager(metall::open_only, dir_path().c_str()); + metall::manager manager(metall::open_only, dir_path()); map_type *map; std::size_t n; std::tie(map, n) = manager.find("map"); @@ -309,7 +310,7 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { } { - metall::manager manager(metall::open_read_only, dir_path().c_str()); + metall::manager manager(metall::open_read_only, dir_path()); map_type *map; std::size_t n; std::tie(map, n) = manager.find("map"); @@ -318,4 +319,5 @@ TEST(StlAllocatorTest, PersistentNestedContainer) { ASSERT_EQ(map->at(0)[1], 2); ASSERT_EQ(map->at(1)[0], 3); } -} \ No newline at end of file +} +} // namespace \ No newline at end of file diff --git a/test/kernel/attributed_object_directory_test.cpp b/test/kernel/attributed_object_directory_test.cpp index c5b59750..3ee0cb87 100644 --- a/test/kernel/attributed_object_directory_test.cpp +++ b/test/kernel/attributed_object_directory_test.cpp @@ -176,7 +176,7 @@ TEST(AttributedObjectDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file(test_utility::make_test_path()); - ASSERT_TRUE(obj.serialize(file.c_str())); + ASSERT_TRUE(obj.serialize(file)); } TEST(AttributedObjectDirectoryTest, Deserialize) { @@ -187,12 +187,12 @@ TEST(AttributedObjectDirectoryTest, Deserialize) { directory_type obj; obj.insert("item1", 1, 2, 5); obj.insert("item2", 3, 4, 6, "description2"); - obj.serialize(file.c_str()); + obj.serialize(file); } { directory_type obj; - ASSERT_TRUE(obj.deserialize(file.c_str())); + ASSERT_TRUE(obj.deserialize(file)); // Get values correctly const auto itr1 = obj.find("item1"); diff --git a/test/kernel/bin_directory_test.cpp b/test/kernel/bin_directory_test.cpp index b1c38a9f..624e5d51 100644 --- a/test/kernel/bin_directory_test.cpp +++ b/test/kernel/bin_directory_test.cpp @@ -98,7 +98,7 @@ TEST(BinDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file = test_utility::make_test_path(); - ASSERT_TRUE(obj.serialize(file.c_str())); + ASSERT_TRUE(obj.serialize(file)); } TEST(BinDirectoryTest, Deserialize) { @@ -114,13 +114,13 @@ TEST(BinDirectoryTest, Deserialize) { obj.insert(num_small_bins - 1, 3); obj.insert(num_small_bins - 1, 4); - obj.serialize(file.c_str()); + obj.serialize(file); } { std::allocator allocator; directory_type obj(allocator); - ASSERT_TRUE(obj.deserialize(file.c_str())); + ASSERT_TRUE(obj.deserialize(file)); #ifdef METALL_USE_SORTED_BIN ASSERT_EQ(obj.front(0), 1); diff --git a/test/kernel/chunk_directory_test.cpp b/test/kernel/chunk_directory_test.cpp index 5e26bc39..8d8fdc64 100644 --- a/test/kernel/chunk_directory_test.cpp +++ b/test/kernel/chunk_directory_test.cpp @@ -124,7 +124,7 @@ TEST(ChunkDirectoryTest, Serialize) { test_utility::create_test_dir(); const auto file(test_utility::make_test_path()); - ASSERT_TRUE(directory.serialize(file.c_str())); + ASSERT_TRUE(directory.serialize(file)); } TEST(ChunkDirectoryTest, Deserialize) { @@ -146,13 +146,13 @@ TEST(ChunkDirectoryTest, Deserialize) { directory.insert(bin_no_mngr::num_small_bins()); // 1 chunk directory.insert(bin_no_mngr::num_small_bins() + 1); // 2 chunks - directory.serialize(file.c_str()); + directory.serialize(file); } { chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4); - ASSERT_TRUE(directory.deserialize(file.c_str())); + ASSERT_TRUE(directory.deserialize(file)); for (uint64_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { const auto bin_no = static_cast(i); const auto chunk_no = static_cast(i); diff --git a/test/kernel/manager_multithread_test.cpp b/test/kernel/manager_multithread_test.cpp index ac742faf..786472a6 100644 --- a/test/kernel/manager_multithread_test.cpp +++ b/test/kernel/manager_multithread_test.cpp @@ -99,7 +99,7 @@ template void run_alloc_dealloc_separated_test(const list_type &allocation_size_list) { // Allocate manager const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); // Main loop std::pair previous_allocation_rage(nullptr, nullptr); @@ -134,7 +134,7 @@ void run_alloc_dealloc_mixed_and_write_value_test( const list_type &allocation_size_list) { // Allocate manager const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); // Main loop std::vector> previous_addr_and_size_array( @@ -295,7 +295,7 @@ TEST(ManagerMultithreadsTest, ConstructAndFind) { constexpr std::size_t num_allocates = 1024; const auto dir(test_utility::make_test_path()); - manager_type manager(metall::create_only, dir.c_str()); + manager_type manager(metall::create_only, dir); std::vector keys; for (std::size_t i = 0; i < num_allocates; ++i) { diff --git a/test/kernel/manager_test.cpp b/test/kernel/manager_test.cpp index 491c8bb3..7c728338 100644 --- a/test/kernel/manager_test.cpp +++ b/test/kernel/manager_test.cpp @@ -5,6 +5,7 @@ #include "gtest/gtest.h" +#include #include #include @@ -12,6 +13,8 @@ #include "../test_utility.hpp" namespace { +namespace fs = std::filesystem; + using namespace metall::mtlldetail; using manager_type = metall::manager; @@ -23,23 +26,21 @@ using object_size_mngr = metall::kernel::object_size_manager; constexpr std::size_t k_min_object_size = object_size_mngr::at(0); -const std::string &dir_path() { - const static std::string path(test_utility::make_test_path()); +const fs::path &dir_path() { + const static fs::path path(test_utility::make_test_path()); return path; } TEST(ManagerTest, CreateAndOpenModes) { { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); ASSERT_TRUE(manager.destroy("int")); } { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto ret = manager.find("int"); ASSERT_EQ(ret.first, nullptr); ASSERT_FALSE(manager.destroy("int")); @@ -47,14 +48,13 @@ TEST(ManagerTest, CreateAndOpenModes) { } { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -63,20 +63,19 @@ TEST(ManagerTest, CreateAndOpenModes) { } { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")(10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -88,14 +87,13 @@ TEST(ManagerTest, CreateAndOpenModes) { TEST(ManagerTest, ConstructArray) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("int")[2](10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -105,7 +103,7 @@ TEST(ManagerTest, ConstructArray) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -114,20 +112,19 @@ TEST(ManagerTest, ConstructArray) { TEST(ManagerTest, findOrConstruct) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.find_or_construct("int")(10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int *a = manager.find_or_construct("int")(20); ASSERT_EQ(*a, 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -136,21 +133,20 @@ TEST(ManagerTest, findOrConstruct) { TEST(ManagerTest, findOrConstructArray) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.find_or_construct("int")[2](10), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int *a = manager.find_or_construct("int")[2](20); ASSERT_EQ(a[0], 10); ASSERT_EQ(a[1], 10); } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -160,16 +156,15 @@ TEST(ManagerTest, ConstructContainers) { { using vec_t = std::vector>; { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_NE(manager.construct("vecs")[2]( 2, 10, manager.get_allocator()), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("vecs"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -183,7 +178,7 @@ TEST(ManagerTest, ConstructContainers) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("vecs")); ASSERT_TRUE(manager.all_memory_deallocated()); } @@ -193,15 +188,14 @@ TEST(ManagerTest, ConstructContainers) { TEST(ManagerTest, ConstructWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values[2] = {10, 20}; ASSERT_NE(manager.construct_it("int")[2](&values[0]), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -211,7 +205,7 @@ TEST(ManagerTest, ConstructWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -226,9 +220,8 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values1[2] = {10, 20}; float values2[2] = {0.1, 0.2}; ASSERT_NE(manager.construct_it("data")[2](&values1[0], &values2[0]), @@ -236,7 +229,7 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); auto ret = manager.find("data"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(ret.second, 2); @@ -248,7 +241,7 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("data")); } } @@ -257,16 +250,15 @@ TEST(ManagerTest, ConstructObjectsWithIterator) { TEST(ManagerTest, FindOrConstructWithIterator) { { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), - 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int values[2] = {10, 20}; ASSERT_NE(manager.find_or_construct_it("int")[2](&values[0]), nullptr); } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); int values[2] = {30, 40}; int *a = manager.find_or_construct_it("int")[2](&values[0]); ASSERT_NE(a, nullptr); @@ -275,7 +267,7 @@ TEST(ManagerTest, FindOrConstructWithIterator) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("int")); } } @@ -283,8 +275,8 @@ TEST(ManagerTest, FindOrConstructWithIterator) { TEST(ManagerTest, Destroy) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_FALSE(manager.destroy("named_obj")); ASSERT_FALSE(manager.destroy(metall::unique_instance)); @@ -307,8 +299,8 @@ TEST(ManagerTest, Destroy) { } { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj")(); manager.construct(metall::unique_instance)(); @@ -317,7 +309,7 @@ TEST(ManagerTest, Destroy) { // Destroy after restoring { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy("named_obj")); ASSERT_TRUE(manager.destroy(metall::unique_instance)); @@ -329,8 +321,8 @@ TEST(ManagerTest, Destroy) { TEST(ManagerTest, DestroyPtr) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int *named_obj = manager.construct("named_obj")(); int *unique_obj = manager.construct(metall::unique_instance)(); @@ -351,8 +343,8 @@ TEST(ManagerTest, DestroyPtr) { } { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj")(); manager.construct(metall::unique_instance)(); @@ -364,7 +356,7 @@ TEST(ManagerTest, DestroyPtr) { // Destroy after restoring { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_TRUE(manager.destroy_ptr(manager.find("named_obj").first)); ASSERT_TRUE( @@ -385,8 +377,8 @@ TEST(ManagerTest, DestroyDestruct) { // -- Check if destructors are called in destroy() -- // { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); int count = 3; auto *data_obj = manager.construct("named_obj")(); @@ -406,8 +398,8 @@ TEST(ManagerTest, DestroyDestruct) { TEST(ManagerTest, GetInstanceName) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_STREQ( manager.get_instance_name(manager.construct("named_obj")()), @@ -424,7 +416,7 @@ TEST(ManagerTest, GetInstanceName) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_STREQ( manager.get_instance_name(manager.find("named_obj").first), "named_obj"); @@ -453,9 +445,9 @@ TEST(ManagerTest, ConstructException) { } }; - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); bool do_throw[2] = {false, true}; bool wrong_destroy = false; bool *flags[2] = {&wrong_destroy, &wrong_destroy}; @@ -477,9 +469,9 @@ TEST(ManagerTest, DestructException) { ~object() noexcept(false) { throw std::runtime_error(""); } }; - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct(metall::unique_instance)(); ASSERT_THROW(manager.destroy(metall::unique_instance), std::exception); @@ -488,8 +480,8 @@ TEST(ManagerTest, DestructException) { TEST(ManagerTest, GetInstanceType) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ(manager.get_instance_kind(manager.construct("named_obj")()), metall::manager::instance_kind::named_kind); @@ -505,7 +497,7 @@ TEST(ManagerTest, GetInstanceType) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_EQ(manager.get_instance_kind(manager.find("named_obj").first), metall::manager::instance_kind::named_kind); @@ -523,8 +515,8 @@ TEST(ManagerTest, GetInstanceType) { TEST(ManagerTest, GetInstanceLength) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ( manager.get_instance_length(manager.construct("named_obj")()), 1); @@ -553,7 +545,7 @@ TEST(ManagerTest, GetInstanceLength) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); { ASSERT_EQ( @@ -584,8 +576,8 @@ TEST(ManagerTest, GetInstanceLength) { TEST(ManagerTest, IsInstanceType) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_TRUE( manager.is_instance_type(manager.construct("named_obj")())); @@ -604,7 +596,7 @@ TEST(ManagerTest, IsInstanceType) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); ASSERT_TRUE( manager.is_instance_type(manager.find("named_obj").first)); @@ -624,8 +616,8 @@ TEST(ManagerTest, IsInstanceType) { TEST(ManagerTest, InstanceDescription) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto *named_obj = manager.construct("named_obj")(); std::string desc_name = "desc name"; @@ -651,7 +643,7 @@ TEST(ManagerTest, InstanceDescription) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); std::string buf; @@ -677,7 +669,7 @@ TEST(ManagerTest, InstanceDescription) { } { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); std::string buf; @@ -706,8 +698,8 @@ TEST(ManagerTest, InstanceDescription) { TEST(ManagerTest, CountObjects) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_EQ(manager.get_num_named_objects(), 0); manager.construct("named_obj1")(); @@ -746,8 +738,8 @@ TEST(ManagerTest, CountObjects) { ptrdiff_t anony_offset1 = 0; ptrdiff_t anony_offset2 = 0; { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); manager.construct("named_obj1")(); manager.construct(metall::unique_instance)(); @@ -762,7 +754,7 @@ TEST(ManagerTest, CountObjects) { } { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); ASSERT_EQ(manager.get_num_named_objects(), 2); ASSERT_TRUE(manager.destroy("named_obj1")); @@ -788,8 +780,8 @@ TEST(ManagerTest, CountObjects) { TEST(ManagerTest, NamedObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -832,8 +824,8 @@ TEST(ManagerTest, NamedObjectIterator) { TEST(ManagerTest, UniqueObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -877,8 +869,8 @@ TEST(ManagerTest, UniqueObjectIterator) { TEST(ManagerTest, AnonymoustObjectIterator) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Everyone is at the end ASSERT_EQ(manager.named_begin(), manager.named_end()); @@ -924,9 +916,9 @@ TEST(ManagerTest, AnonymoustObjectIterator) { } TEST(ManagerTest, GetSegment) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); auto *obj = manager.construct(metall::unique_instance)(); ASSERT_EQ(manager.unique_begin()->offset() + static_cast(manager.get_address()), @@ -935,46 +927,46 @@ TEST(ManagerTest, GetSegment) { } TEST(ManagerTest, Consistency) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Must be inconsistent before closing - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); manager.construct("dummy")(10); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { // To make sure the consistent mark is cleared even after creating a new // data store using an old dir path - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); manager.construct("dummy")(10); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + manager_type manager(metall::open_only, dir_path()); + ASSERT_FALSE(manager_type::consistent(dir_path())); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); { - manager_type manager(metall::open_read_only, dir_path().c_str()); + manager_type manager(metall::open_read_only, dir_path()); // Still consistent if it is opened with the read-only mode - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); } - ASSERT_TRUE(manager_type::consistent(dir_path().c_str())); + ASSERT_TRUE(manager_type::consistent(dir_path())); } TEST(ManagerTest, TinyAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); const std::size_t alloc_size = k_min_object_size / 2; @@ -995,8 +987,8 @@ TEST(ManagerTest, TinyAllocation) { TEST(ManagerTest, SmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); const std::size_t alloc_size = k_min_object_size; @@ -1017,8 +1009,8 @@ TEST(ManagerTest, SmallAllocation) { TEST(ManagerTest, AllSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); for (std::size_t s = 1; s < k_chunk_size; ++s) manager.deallocate(manager.allocate(s)); } @@ -1026,8 +1018,8 @@ TEST(ManagerTest, AllSmallAllocation) { TEST(ManagerTest, MaxSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); // Max small allocation size const std::size_t alloc_size = @@ -1057,8 +1049,8 @@ TEST(ManagerTest, MaxSmallAllocation) { TEST(ManagerTest, MixedSmallAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); const std::size_t alloc_size1 = k_min_object_size * 2; const std::size_t alloc_size2 = k_min_object_size * 4; @@ -1097,8 +1089,8 @@ TEST(ManagerTest, MixedSmallAllocation) { TEST(ManagerTest, LargeAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); // Assume that the object cache is not used for large allocation char *base_addr = nullptr; @@ -1132,8 +1124,8 @@ TEST(ManagerTest, LargeAllocation) { TEST(ManagerTest, AllMemoryDeallocated) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); ASSERT_TRUE(manager.all_memory_deallocated()); @@ -1153,8 +1145,8 @@ TEST(ManagerTest, AllMemoryDeallocated) { TEST(ManagerTest, AlignedAllocation) { { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); for (std::size_t alignment = k_min_object_size; alignment <= k_chunk_size; alignment *= 2) { @@ -1208,21 +1200,20 @@ TEST(ManagerTest, AlignedAllocation) { } TEST(ManagerTest, Flush) { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); manager.construct("int")(10); manager.flush(); - ASSERT_FALSE(manager_type::consistent(dir_path().c_str())); + ASSERT_FALSE(manager_type::consistent(dir_path())); } TEST(ManagerTest, AnonymousConstruct) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type *manager; - manager = - new manager_type(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager = new manager_type(metall::create_only, dir_path(), 1UL << 30UL); int *const a = manager->construct(metall::anonymous_instance)(); ASSERT_NE(a, nullptr); @@ -1238,10 +1229,9 @@ TEST(ManagerTest, AnonymousConstruct) { } TEST(ManagerTest, UniqueConstruct) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type *manager; - manager = - new manager_type(metall::create_only, dir_path().c_str(), 1UL << 30UL); + manager = new manager_type(metall::create_only, dir_path(), 1UL << 30UL); int *const a = manager->construct(metall::unique_instance)(); ASSERT_NE(a, nullptr); @@ -1263,44 +1253,44 @@ TEST(ManagerTest, UniqueConstruct) { } TEST(ManagerTest, UUID) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); std::string uuid; { - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type manager(metall::create_only, dir_path()); - uuid = manager_type::get_uuid(dir_path().c_str()); + uuid = manager_type::get_uuid(dir_path()); ASSERT_FALSE(uuid.empty()); } { // Returns the same UUID? - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_uuid(dir_path().c_str()), uuid); + manager_type manager(metall::open_only, dir_path()); + ASSERT_EQ(manager_type::get_uuid(dir_path()), uuid); } { // Returns a new UUID? - manager_type manager(metall::create_only, dir_path().c_str()); - ASSERT_NE(manager_type::get_uuid(dir_path().c_str()), uuid); + manager_type manager(metall::create_only, dir_path()); + ASSERT_NE(manager_type::get_uuid(dir_path()), uuid); } } TEST(ManagerTest, Version) { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); { - manager_type manager(metall::create_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_version(dir_path().c_str()), METALL_VERSION); + manager_type manager(metall::create_only, dir_path()); + ASSERT_EQ(manager_type::get_version(dir_path()), METALL_VERSION); } { - manager_type manager(metall::open_only, dir_path().c_str()); - ASSERT_EQ(manager_type::get_version(dir_path().c_str()), METALL_VERSION); + manager_type manager(metall::open_only, dir_path()); + ASSERT_EQ(manager_type::get_version(dir_path()), METALL_VERSION); } } TEST(ManagerTest, Description) { // Set and get with non-static method { - manager_type::remove(dir_path().c_str()); - manager_type manager(metall::create_only, dir_path().c_str()); + manager_type::remove(dir_path()); + manager_type manager(metall::create_only, dir_path()); ASSERT_TRUE(manager.set_description("description1")); std::string description; @@ -1311,23 +1301,21 @@ TEST(ManagerTest, Description) { // Get with static method { std::string description; - ASSERT_TRUE( - manager_type::get_description(dir_path().c_str(), &description)); + ASSERT_TRUE(manager_type::get_description(dir_path(), &description)); ASSERT_STREQ(description.c_str(), "description1"); } // Set with static method { - manager_type::remove(dir_path().c_str()); + manager_type::remove(dir_path()); manager_type manager(metall::create_only, - dir_path().c_str()); // Make a new data store - ASSERT_TRUE( - manager_type::set_description(dir_path().c_str(), "description2")); + dir_path()); // Make a new data store + ASSERT_TRUE(manager_type::set_description(dir_path(), "description2")); } // Get with non-static method { - manager_type manager(metall::open_only, dir_path().c_str()); + manager_type manager(metall::open_only, dir_path()); std::string description; ASSERT_TRUE(manager.get_description(&description)); ASSERT_STREQ(description.c_str(), "description2"); @@ -1341,13 +1329,13 @@ TEST(ManagerTest, CheckSanity) { metall::logger::abort_on_critical_error(false); { - auto *manager = new manager_type(metall::create_only, dir_path().c_str()); + auto *manager = new manager_type(metall::create_only, dir_path()); ASSERT_TRUE(manager->check_sanity()); } { auto *bad_manager = - new manager_type(metall::open_only, (dir_path() + "-invalid").c_str()); + new manager_type(metall::open_only, dir_path().string() + "-invalid"); ASSERT_FALSE(bad_manager->check_sanity()); } } diff --git a/test/kernel/multimanager_test.cpp b/test/kernel/multimanager_test.cpp index 26b4140b..23211406 100644 --- a/test/kernel/multimanager_test.cpp +++ b/test/kernel/multimanager_test.cpp @@ -34,9 +34,9 @@ TEST(MultiManagerTest, SingleThread) { const auto dir_path2(test_utility::make_test_path(std::to_string(2))); { - manager_type manager1(metall::create_only, dir_path1.c_str(), + manager_type manager1(metall::create_only, dir_path1, k_chunk_size * 8); - manager_type manager2(metall::create_only, dir_path2.c_str(), + manager_type manager2(metall::create_only, dir_path2, k_chunk_size * 8); vector_type *vector1 = @@ -52,8 +52,8 @@ TEST(MultiManagerTest, SingleThread) { } { - manager_type manager1(metall::open_only, dir_path1.c_str()); - manager_type manager2(metall::open_only, dir_path2.c_str()); + manager_type manager1(metall::open_only, dir_path1); + manager_type manager2(metall::open_only, dir_path2); vector_type *vector1; std::size_t n1; @@ -73,8 +73,8 @@ TEST(MultiManagerTest, SingleThread) { } { - manager_type manager1(metall::open_only, dir_path1.c_str()); - manager_type manager2(metall::open_only, dir_path2.c_str()); + manager_type manager1(metall::open_only, dir_path1); + manager_type manager2(metall::open_only, dir_path2); vector_type *vector1; std::size_t n1; @@ -111,7 +111,7 @@ TEST(MultiManagerTest, MultiThread) { const auto dir_path(test_utility::make_test_path( "/" + std::to_string(omp::get_thread_num()))); - manager_type manager(metall::create_only, dir_path.c_str(), + manager_type manager(metall::create_only, dir_path, k_chunk_size * 16); vector_type *vector = manager.construct("vector")(manager.get_allocator<>()); @@ -123,7 +123,7 @@ TEST(MultiManagerTest, MultiThread) { for (int t = 0; t < get_num_threads(); ++t) { const auto dir_path(test_utility::make_test_path("/" + std::to_string(t))); - manager_type manager(metall::open_only, dir_path.c_str()); + manager_type manager(metall::open_only, dir_path); vector_type *vector; std::size_t n; diff --git a/test/kernel/object_attribute_accessor_test.cpp b/test/kernel/object_attribute_accessor_test.cpp index e1f9b96f..335be02e 100644 --- a/test/kernel/object_attribute_accessor_test.cpp +++ b/test/kernel/object_attribute_accessor_test.cpp @@ -15,24 +15,24 @@ using namespace metall; auto attr_accessor_named() { return manager::access_named_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } auto attr_accessor_unique() { return manager::access_unique_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } auto attr_accessor_anonymous() { return manager::access_anonymous_object_attribute( - test_utility::make_test_path().c_str()); + test_utility::make_test_path()); } TEST(ObjectAttributeAccessorTest, Constructor) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } @@ -42,10 +42,10 @@ TEST(ObjectAttributeAccessorTest, Constructor) { } TEST(ObjectAttributeAccessorTest, NumObjects) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } @@ -56,7 +56,7 @@ TEST(ObjectAttributeAccessorTest, NumObjects) { } { - manager mngr(open_only, test_utility::make_test_path().c_str()); + manager mngr(open_only, test_utility::make_test_path()); mngr.construct("int1")(); mngr.construct("int2")(); mngr.construct(unique_instance)(); @@ -71,10 +71,10 @@ TEST(ObjectAttributeAccessorTest, NumObjects) { } TEST(ObjectAttributeAccessorTest, Count) { - manager::remove(test_utility::make_test_path().c_str()); + manager::remove(test_utility::make_test_path()); { - manager mngr(create_only, test_utility::make_test_path().c_str(), + manager mngr(create_only, test_utility::make_test_path(), 1ULL << 30ULL); } diff --git a/test/kernel/snapshot_test.cpp b/test/kernel/snapshot_test.cpp index 3f4b25cf..f0ad8175 100644 --- a/test/kernel/snapshot_test.cpp +++ b/test/kernel/snapshot_test.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" #include +#include #include @@ -13,42 +14,44 @@ namespace { -std::string original_dir_path() { - const std::string path(test_utility::make_test_path("original")); +namespace fs = std::filesystem; + +fs::path original_dir_path() { + const fs::path path(test_utility::make_test_path("original")); return path; } -std::string snapshot_dir_path() { - const std::string path(test_utility::make_test_path("snapshot")); +fs::path snapshot_dir_path() { + const fs::path path(test_utility::make_test_path("snapshot")); return path; } TEST(SnapshotTest, Snapshot) { - metall::manager::remove(original_dir_path().c_str()); - metall::manager::remove(snapshot_dir_path().c_str()); + metall::manager::remove(original_dir_path()); + metall::manager::remove(snapshot_dir_path()); { - metall::manager manager(metall::create_only, original_dir_path().c_str()); + metall::manager manager(metall::create_only, original_dir_path()); [[maybe_unused]] auto a = manager.construct("a")(1); [[maybe_unused]] auto b = manager.construct(metall::unique_instance)(2); - ASSERT_TRUE(manager.snapshot(snapshot_dir_path().c_str())); - ASSERT_TRUE(metall::manager::consistent(snapshot_dir_path().c_str())); + ASSERT_TRUE(manager.snapshot(snapshot_dir_path())); + ASSERT_TRUE(metall::manager::consistent(snapshot_dir_path())); // UUID const auto original_uuid = - metall::manager::get_uuid(original_dir_path().c_str()); + metall::manager::get_uuid(original_dir_path()); ASSERT_FALSE(original_uuid.empty()); const auto snapshot_uuid = - metall::manager::get_uuid(snapshot_dir_path().c_str()); + metall::manager::get_uuid(snapshot_dir_path()); ASSERT_FALSE(snapshot_uuid.empty()); ASSERT_NE(original_uuid, snapshot_uuid); // Version - ASSERT_EQ(metall::manager::get_version(original_dir_path().c_str()), - metall::manager::get_version(snapshot_dir_path().c_str())); + ASSERT_EQ(metall::manager::get_version(original_dir_path()), + metall::manager::get_version(snapshot_dir_path())); } { diff --git a/test/test_utility.hpp b/test/test_utility.hpp index fd656bd8..f8bae3b4 100644 --- a/test/test_utility.hpp +++ b/test/test_utility.hpp @@ -10,20 +10,26 @@ #include #include +#include_next +#include #include namespace test_utility { +namespace { +namespace fs = std::filesystem; +} + const char *k_test_dir_env_name = "METALL_TEST_DIR"; const char *k_default_test_dir = "/tmp/metall_test_dir"; namespace detail { -inline std::string get_test_dir() { +inline fs::path get_test_dir() { if (const char *env_p = std::getenv(k_test_dir_env_name)) { - return std::string(env_p); + return fs::path(env_p); } - return std::string(k_default_test_dir); + return fs::path(k_default_test_dir); } } // namespace detail @@ -33,11 +39,14 @@ inline bool create_test_dir() { return true; } -inline std::string make_test_path(const std::string &name = std::string()) { - return detail::get_test_dir() + "/metalltest" + "-" + - ::testing::UnitTest::GetInstance()->current_test_case()->name() + "-" + - ::testing::UnitTest::GetInstance()->current_test_info()->name() + "-" + - name; +inline fs::path make_test_path(const fs::path &name = fs::path()) { + std::stringstream file_name; + file_name << "metalltest-" + << ::testing::UnitTest::GetInstance()->current_test_case()->name() + << "-" + << ::testing::UnitTest::GetInstance()->current_test_info()->name() + << "-" << name.string(); + return detail::get_test_dir() / file_name.str(); } } // namespace test_utility diff --git a/tutorial/hands_on/README.md b/tutorial/hands_on/README.md index a520303b..58243064 100644 --- a/tutorial/hands_on/README.md +++ b/tutorial/hands_on/README.md @@ -117,22 +117,9 @@ g++ -std=c++17 [tutorial_program.cpp] -lstdc++fs -I../../include -I${BOOST_ROOT} Clang (or Apple clang) could be used instead of GCC to build Metall. However, please note that we haven't tested it intensively. - - -**On macOS >= 10.15 or Linux** +To run on macOS, Metall requires macOS >= 10.15. ```bash # Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB ``` \ No newline at end of file diff --git a/tutorial/nvmw21/README.md b/tutorial/nvmw21/README.md index df098202..c71ec21d 100644 --- a/tutorial/nvmw21/README.md +++ b/tutorial/nvmw21/README.md @@ -67,22 +67,9 @@ g++ -std=c++17 [tutorial_program.cpp] -lstdc++fs -I../../include -I${BOOST_ROOT} Clang (or Apple clang) could be used instead of GCC to build Metall. However, please note that we haven't tested it intensively. - - -**On macOS >= 10.15 or Linux** +To run on macOS, Metall requires macOS >= 10.15. ```bash # Remove "-lstdc++fs" option clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -``` - - -**On macOS < 10.15** - -The C++17 library is not available on macOS < 10.15. -One has to stop using C++17 library in Metall. -If METALL_DISABLE_CXX17_FILESYSTEM_LIB macro is defined, Metall uses its own file system operation implementation. - -```bash -clang++ -std=c++17 [tutorial_program.cpp] -I../../include -I${BOOST_ROOT} -DMETALL_DISABLE_CXX17_FILESYSTEM_LIB ``` \ No newline at end of file diff --git a/verification/CMakeLists.txt b/verification/CMakeLists.txt index 66d65804..819839da 100644 --- a/verification/CMakeLists.txt +++ b/verification/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(mmap) add_subdirectory(object_size) add_subdirectory(logger) add_subdirectory(soft_dirty) -add_subdirectory(file_io) \ No newline at end of file +add_subdirectory(file_io) +add_subdirectory(sparse_copy) \ No newline at end of file diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt new file mode 100644 index 00000000..ac13e8f9 --- /dev/null +++ b/verification/sparse_copy/CMakeLists.txt @@ -0,0 +1 @@ +add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy.cpp b/verification/sparse_copy/verify_sparse_copy.cpp new file mode 100644 index 00000000..d69e45f5 --- /dev/null +++ b/verification/sparse_copy/verify_sparse_copy.cpp @@ -0,0 +1,87 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include +#include +#include + +#include +#include + +namespace mdtl = metall::mtlldetail; + +int main() { + const std::filesystem::path src_path = "source.dat"; + const ssize_t size = 1024 * 1024 * 32; + + if (!mdtl::create_file(src_path)) { + std::cerr << "Failed to create a file" << std::endl; + return EXIT_FAILURE; + } + + if (!mdtl::extend_file_size(src_path, size)) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + { + auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); + if (!map) { + std::cerr << "Failed to map a file" << std::endl; + return EXIT_FAILURE; + } + auto* buf = reinterpret_cast(map); + buf[0] = 1; + buf[1024 * 1024 - 1] = 1; + if (!mdtl::munmap(fd, map, size, true)) { + std::cerr << "Failed to unmap a file" << std::endl; + return EXIT_FAILURE; + } + } + + if (mdtl::get_file_size(src_path) < size) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + if (mdtl::get_actual_file_size(src_path) >= size) { + std::cerr << "Failed to create a sparse file" << std::endl; + std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) + << std::endl; + return EXIT_FAILURE; + } + + // Sparse copy + const std::filesystem::path dst_path = "destination.dat"; + if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { + std::cerr << "Failed to copy a file" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Source file size: " << mdtl::get_file_size(src_path) + << std::endl; + std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) + << std::endl; + + // Show actual size + std::cout << "Source actual file size: " + << mdtl::get_actual_file_size(src_path) << std::endl; + std::cout << "Destination actual file size: " + << mdtl::get_actual_file_size(dst_path) << std::endl; + + // if (mdtl::get_file_size(dst_path) < size) { + // std::cerr << "Failed to extend file size" << std::endl; + // return EXIT_FAILURE; + // } + +// if (mdtl::get_actual_file_size(dst_path) >= size) { +// std::cerr << "Failed to create a sparse file" << std::endl; +// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) +// << std::endl; +// return EXIT_FAILURE; +// } + + return 0; +} \ No newline at end of file From 92c7005e37a767605ea9115db586019bd68291ac Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:00:22 +0100 Subject: [PATCH 14/26] implement linux copy algorithms using syscalls --- include/metall/detail/file.hpp | 264 ++++++++++- include/metall/detail/file_clone.hpp | 121 +++-- include/metall/detail/soft_dirty_page.hpp | 1 + verification/soft_dirty/verify_soft_dirty.cpp | 1 + verification/sparse_copy/CMakeLists.txt | 1 + .../sparse_copy/verify_sparse_copy.cpp | 421 +++++++++++++++--- .../sparse_copy/verify_sparse_copy_manual.cpp | 87 ++++ 7 files changed, 779 insertions(+), 117 deletions(-) create mode 100644 verification/sparse_copy/verify_sparse_copy_manual.cpp diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index d351d035..94675430 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -377,20 +378,241 @@ inline bool copy_file_sparse_manually(const fs::path &source_path, } #ifdef __linux__ -inline bool copy_file_sparse_linux(const fs::path &source_path, - const fs::path &destination_path) { - std::string command("cp --sparse=auto " + source_path.string() + " " + - destination_path.string()); - const int status = std::system(command.c_str()); - const bool success = (status != -1) && !!(WIFEXITED(status)); - if (!success) { + +/** + * \brief Prepares a file copy from source_path to destination_path + * by opening/creating the relevant files and setting appropriate permissions. + * + * \param source_path path to source file + * \param destination_path desired path of destination file + * \param src out parameter for the file descriptor opened for source_path (will be opened read only) + * \param dst out parameter for the file descriptor opened for destination_path (will be opened write only) + * \return on success: size of source file as obtained by ::fstat. on failure: -1 + * + * \warning if the function fails the user must not use the obtained src and dst file descriptors in any way + * (they don't need to be closed) + */ +inline off_t prepare_file_copy_linux(const std::filesystem::path &source_path, + const std::filesystem::path &destination_path, + int *src, + int *dst) { + *src = ::open(source_path.c_str(), O_RDONLY); + if (*src == -1) { std::stringstream ss; - ss << "Failed copying file: " << source_path << " -> " << destination_path; - logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + ss << "Unable to open " << source_path; + logger::perror(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + return -1; + } + + struct stat st; + if (::fstat(*src, &st) == -1) { + std::stringstream ss; + ss << "Unable to stat " << source_path; + logger::perror(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + os_close(*src); + return -1; + } + + *dst = ::open(destination_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); + if (*dst == -1) { + std::stringstream ss; + ss << "Unable to open " << destination_path; + logger::perror(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + os_close(*src); + return -1; + } + + return st.st_size; +} + +/** + * @brief Performs an accelerated, in-kernel copy from src to dst + * @param src source file descriptor + * @param dst destination file descriptor + * @param src_size size of source file as obtained by ::fstat(src) + * @return if the operation was successful + * + * Relevant man pages: + * - https://www.man7.org/linux/man-pages/man2/copy_file_range.2.html + */ +inline bool copy_file_dense_linux(const int src, const int dst, const off_t src_size) { + if (::copy_file_range(src, nullptr, dst, nullptr, src_size, 0) < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "copy_file_range"); return false; } - return success; + + return true; } + +/** + * @brief performs a dense copy from source_path to destionation_path + * @param source_path path to source file + * @param destination_path path to destination file + * @return if the operation was successful + */ +inline bool copy_file_dense_linux(const std::filesystem::path &source_path, + const std::filesystem::path &destination_path) { + int src; + int dst; + const off_t src_size = prepare_file_copy_linux(source_path, destination_path, &src, &dst); + if (src_size >= 0) { + if (copy_file_dense_linux(src, dst, src_size)) { + os_fsync(dst); + os_close(src); + os_close(dst); + return true; + } + } + + os_close(src); + os_close(dst); + logger::out(logger::level::warning, __FILE__, __LINE__, "Unable to use accelerated dense copy, falling back to unaccelerated dense copy"); + + return copy_file_dense(source_path, destination_path); +} + +/** + * Creates a hole of size size at the current cursor of fd. + * Moves cursor of fd behind the created hole. + * + * \param fd file descriptor + * \param size size of to-be-created hole + * \return if creation was successful + * \note the cursor position will still be advanced even if hole punching fails + * + * Relevant man pages: + * https://www.man7.org/linux/man-pages/man2/lseek.2.html + * https://man7.org/linux/man-pages/man2/fallocate.2.html + */ +inline bool create_hole_linux(const int fd, const off_t size) { + if (size == 0) { + return true; + } + + // Seek size bytes past the current position + const off_t hole_end = ::lseek(fd, size, SEEK_CUR); + if (hole_end < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "lseek"); + return false; + } + + // punch a hole from old cursor to new cursor + if (::fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, hole_end - size, size) < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "fallocate(FALLOC_FL_PUNCH_HOLE)"); + return false; + } + + return true; +} + +/** + * Performs a sparse copy from src to dst, by only copying actual data and manually recreating + * all holes from src in dst. + * + * I.e. for each "segment" (data segment or hole) in src do + * if segment is data => copy segment to dst + * if segment is hole => create new hole (of the appropriate size) in dst + * + * \param src source file descriptor + * \param dst destination file descriptor + * \param src_size size of file behind src as obtained by ::fstat + * \return if copying was successful + * + * Relevant man pages: + * https://www.man7.org/linux/man-pages/man2/lseek.2.html + * https://www.man7.org/linux/man-pages/man2/copy_file_range.2.html + * https://www.man7.org/linux/man-pages/man3/ftruncate.3p.html + */ +inline bool copy_file_sparse_linux(const int src, const int dst, const off_t src_size) { + off_t old_off = 0; + off_t off = 0; + + while ((off = ::lseek(src, off, SEEK_DATA)) >= 0) { + if (!create_hole_linux(dst, off - old_off)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Unable to punch hole"); + return false; + } + + off_t const hole_start = ::lseek(src, off, SEEK_HOLE); + if (hole_start < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "fseek(SEEK_HOLE)"); + return false; + } + + if (::copy_file_range(src, &off, dst, nullptr, hole_start - off, 0) < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "copy_file_range"); + return false; + } + + old_off = off; + } + + if (errno != ENXIO) { + // error condition: offset is _not_ within a hole at the end of the file. + // previous lseek from while-loop condition must have failed + logger::perror(logger::level::error, __FILE__, __LINE__, "fseek(SEEK_DATA)"); + return false; + } + + if (old_off < src_size) { + // the final extent is a hole we must call ftruncate + // here in order to record the proper length in the destination. + // See also: https://github.com/coreutils/coreutils/blob/a257b63ce7ebcc4577adb5406b39fc0edd61dcac/src/copy.c#L643-L658 + if (::ftruncate(dst, src_size) < 0) { + logger::perror(logger::level::error, __FILE__, __LINE__, "ftruncate"); + return false; + } + + if (!create_hole_linux(dst, src_size - old_off)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Unable to punch hole"); + return false; + } + } + + return true; +} + +/** + * Attempts to perform a sparse copy from source_path to destination_path, + * falling back to regular copy if the sparse copy fails. + */ +inline bool copy_file_sparse_linux(const std::filesystem::path &source_path, + const std::filesystem::path &destination_path) { + int src; + int dst; + const off_t src_size = prepare_file_copy_linux(source_path, destination_path, &src, &dst); + if (src_size < 0) { + logger::out(logger::level::error, __FILE__, __LINE__, "Unable to prepare for file copy"); + return false; + } + + if (copy_file_sparse_linux(src, dst, src_size)) { + os_fsync(dst); + os_close(src); + os_close(dst); + return true; + } + + { + std::stringstream ss; + ss << "Unable to sparse copy " << source_path << " to " << destination_path << ", falling back to normal copy"; + logger::out(logger::level::warning, __FILE__, __LINE__, ss.str().c_str()); + } + + os_close(src); + os_close(dst); + + if (copy_file_dense_linux(source_path, destination_path)) { + return true; + } + + std::stringstream ss; + ss << "Unable to copy " << source_path << " to " << destination_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + + return false; +} + #endif } // namespace fcpdtl @@ -407,11 +629,16 @@ inline bool copy_file(const fs::path &source_path, #ifdef __linux__ return fcpdtl::copy_file_sparse_linux(source_path, destination_path); #else - logger::out(logger::level::info, __FILE__, __LINE__, + logger::out(logger::level::warning, __FILE__, __LINE__, "Sparse file copy is not available"); #endif } + +#if __linux__ + return fcpdtl::copy_file_dense_linux(source_path, destination_path); +#else return fcpdtl::copy_file_dense(source_path, destination_path); +#endif } /// \brief Get the file names in a directory. @@ -479,17 +706,18 @@ inline bool copy_files_in_directory_in_parallel_helper( } }; - const auto num_threads = (int)std::min( + const auto num_threads = std::min( src_file_names.size(), - (std::size_t)(max_num_threads > 0 ? max_num_threads - : std::thread::hardware_concurrency())); - std::vector threads(num_threads, nullptr); - for (auto &th : threads) { - th = new std::thread(copy_lambda); + max_num_threads > 0 ? max_num_threads + : std::thread::hardware_concurrency()); + std::vector threads; + threads.reserve(num_threads); + for (std::size_t ix = 0; ix < num_threads; ++ix) { + threads.emplace_back(copy_lambda); } for (auto &th : threads) { - th->join(); + th.join(); } return num_successes == src_file_names.size(); diff --git a/include/metall/detail/file_clone.hpp b/include/metall/detail/file_clone.hpp index 81d3d071..fd89b83c 100644 --- a/include/metall/detail/file_clone.hpp +++ b/include/metall/detail/file_clone.hpp @@ -29,13 +29,77 @@ namespace fs = std::filesystem; namespace file_clone_detail { #ifdef __linux__ -inline bool clone_file_linux(const fs::path &source_path, - const fs::path &destination_path) { - std::string command("cp --reflink=auto -R " + source_path.string() + " " + - destination_path.string()); - const int status = std::system(command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); + +/** + * Clone file using + * https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html + */ +inline bool clone_file_linux(const int src, const int dst) { +#ifdef FICLONE + return ::ioctl(dst, FICLONE, src) != -1; +#else + errno = ENOTSUP; + return false; +#endif // defined(FICLONE) +} + +/** + * Attempts to perform an O(1) clone of source_path to destionation_path + * if cloning fails, falls back to sparse copying + * if sparse copying fails, falls back to regular copying + */ +inline bool clone_file_linux(const std::filesystem::path &source_path, + const std::filesystem::path &destination_path) { + int src; + int dst; + const off_t src_size = fcpdtl::prepare_file_copy_linux(source_path, destination_path, &src, &dst); + if (src_size < 0) { + logger::out(logger::level::error, __FILE__, __LINE__, "Unable to prepare for file copy"); + return false; + } + + const auto close_fsync_all = [&]() noexcept { + os_fsync(dst); + os_close(src); + os_close(dst); + }; + + if (clone_file_linux(src, dst)) { + close_fsync_all(); + return true; + } + + { + std::stringstream ss; + ss << "Unable to clone " << source_path << " to " << destination_path << ", falling back to sparse copy"; + logger::out(logger::level::warning, __FILE__, __LINE__, ss.str().c_str()); + } + + if (fcpdtl::copy_file_sparse_linux(src, dst, src_size)) { + close_fsync_all(); + return true; + } + + { + std::stringstream ss; + ss << "Unable to sparse copy " << source_path << " to " << destination_path << ", falling back to normal copy"; + logger::out(logger::level::warning, __FILE__, __LINE__, ss.str().c_str()); + } + + os_close(src); + os_close(dst); + + if (fcpdtl::copy_file_dense_linux(source_path, destination_path)) { + return true; + } + + std::stringstream ss; + ss << "Unable to copy " << source_path << " to " << destination_path; + logger::out(logger::level::error, __FILE__, __LINE__, ss.str().c_str()); + + return false; } + #endif #ifdef __APPLE__ @@ -44,7 +108,17 @@ inline bool clone_file_macos(const fs::path &source_path, std::string command("cp -cR " + source_path.string() + " " + destination_path.string()); const int status = std::system(command.c_str()); - return (status != -1) && !!(WIFEXITED(status)); + if ((status != -1) && !!(WIFEXITED(status))) { + return true; + } + + { + std::stringstream ss; + ss << "Unable to sparse copy " << source_path << " to " << destination_path << ", falling back to normal copy"; + logger::out(logger::level::warning, __FILE__, __LINE__, ss.str().c_str()); + } + + return fcpdtl::copy_file_dense(source_path, destination_path); } #endif } // namespace file_clone_detail @@ -55,40 +129,21 @@ inline bool clone_file_macos(const fs::path &source_path, /// error, returns false. inline bool clone_file(const fs::path &source_path, const fs::path &destination_path) { - bool ret = false; #if defined(__linux__) - ret = file_clone_detail::clone_file_linux(source_path, destination_path); - if (!ret) { - std::string s("On Linux, Failed to clone " + source_path.string() + " to " + - destination_path.string()); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - } + return file_clone_detail::clone_file_linux(source_path, destination_path); #elif defined(__APPLE__) - ret = file_clone_detail::clone_file_macos(source_path, destination_path); - if (!ret) { - std::string s("On MacOS, Failed to clone " + source_path.string() + " to " + - destination_path.string()); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - } + return file_clone_detail::clone_file_macos(source_path, destination_path); #else + #ifdef METALL_VERBOSE_SYSTEM_SUPPORT_WARNING #warning "Copy file normally instead of cloning" -#endif - logger::out(logger::level::warning, __FILE__, __LINE__, - "Use normal copy instead of clone"); - ret = copy_file(source_path, destination_path); // Copy normally - if (!ret) { - std::string s("Failed to copy " + source_path.string() + " to " + - destination_path.string()); - logger::out(logger::level::error, __FILE__, __LINE__, s.c_str()); - } #endif - if (ret) { - ret &= metall::mtlldetail::fsync(destination_path); - } + logger::out(logger::level::warning, __FILE__, __LINE__, + "Using normal copy instead of clone"); + return copy_file(source_path, destination_path); - return ret; +#endif } /// \brief Clone files in a directory. diff --git a/include/metall/detail/soft_dirty_page.hpp b/include/metall/detail/soft_dirty_page.hpp index d8274510..df18a484 100644 --- a/include/metall/detail/soft_dirty_page.hpp +++ b/include/metall/detail/soft_dirty_page.hpp @@ -6,6 +6,7 @@ #ifndef METALL_DETAIL_UTILITY_SOFT_DIRTY_PAGE_HPP #define METALL_DETAIL_UTILITY_SOFT_DIRTY_PAGE_HPP +#include #include #include #include diff --git a/verification/soft_dirty/verify_soft_dirty.cpp b/verification/soft_dirty/verify_soft_dirty.cpp index c6b129f3..e485ccc1 100644 --- a/verification/soft_dirty/verify_soft_dirty.cpp +++ b/verification/soft_dirty/verify_soft_dirty.cpp @@ -7,6 +7,7 @@ #include #include #include +#include bool run_in_core_test(const ssize_t page_size, const std::size_t num_pages, char *const map) { diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt index ac13e8f9..d1db9b43 100644 --- a/verification/sparse_copy/CMakeLists.txt +++ b/verification/sparse_copy/CMakeLists.txt @@ -1 +1,2 @@ +add_metall_executable(verify_sparse_copy_manual verify_sparse_copy_manual.cpp) add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy.cpp b/verification/sparse_copy/verify_sparse_copy.cpp index d69e45f5..0811d68a 100644 --- a/verification/sparse_copy/verify_sparse_copy.cpp +++ b/verification/sparse_copy/verify_sparse_copy.cpp @@ -1,87 +1,376 @@ -// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall -// Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (Apache-2.0 OR MIT) +#undef NDEBUG -#include -#include -#include +#include #include -#include -namespace mdtl = metall::mtlldetail; +#include +#include + +std::filesystem::path make_test_path(); +void randomized_copy_file_sparse_linux(std::filesystem::path const &p); +void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p); int main() { - const std::filesystem::path src_path = "source.dat"; - const ssize_t size = 1024 * 1024 * 32; + auto p = make_test_path(); - if (!mdtl::create_file(src_path)) { - std::cerr << "Failed to create a file" << std::endl; - return EXIT_FAILURE; - } + randomized_copy_file_sparse_linux(p); + adjacent_hole_copy_file_sparse_linux(p); +} + + +// Utility + +constexpr size_t FILE_SIZE = 4096*4*11; +std::default_random_engine rng{std::random_device{}()}; + +/** + * Fills file at fd with FILE_SIZE random bytes + */ +void fill_file(int fd); + +/** + * Randomly punches 1-9 holes into fd. + * + * @param hole_at_start forces a hole to be created at the start of the file + * @param hole_at_end forces a hole to be created at the end of the file + */ +void punch_holes(int fd, bool hole_at_start = false, bool hole_at_end = false); + +/** + * Convenience wrapper around ::mmap + * @param fd file descriptor to mmap + * @return pair (pointer to mapped range, size of mapped range) + */ +std::pair mmap(int fd); + +/** + * Checks if files a and b are equal byte by byte + */ +void check_files_eq(int a, int b); + +/** + * Returns a list of all holes in the given file + */ +std::vector> get_holes(int fd); + +/** + * print all given holes + */ + void list_holes(std::vector> const &holes); + +/** + * Checks if the holes in a and b are in the same places and of the same size + */ +void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b); - if (!mdtl::extend_file_size(src_path, size)) { - std::cerr << "Failed to extend file size" << std::endl; - return EXIT_FAILURE; +/** + * Runs the test + */ +template +void sparse_copy_test(std::filesystem::path const &srcp, + std::filesystem::path const &dstp, + std::filesystem::path const &dst2p, + P &&punch_holes); + + +// Impl + +std::filesystem::path make_test_path() { + std::filesystem::path p{"/tmp/metallsparsecopytest" + std::to_string(rng())}; + std::filesystem::create_directory(p); + return p; +} + +void randomized_copy_file_sparse_linux(std::filesystem::path const &p) { + auto srcp = p / "copy_file_sparse-src.bin"; + auto dstp = p / "copy_file_sparse-dst.bin"; + auto dst2p = p / "copy_file_sparse-dst2.bin"; + + std::uniform_int_distribution b{0, 1}; + + for (size_t ix = 0; ix < 1000; ++ix) { + sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { + punch_holes(fd, b(rng), b(rng)); + }); } + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); +} + +void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p) { + auto srcp = p / "adj-copy_file_sparse-src.bin"; + auto dstp = p / "adj-copy_file_sparse-dst.bin"; + auto dst2p = p / "adj-copy_file_sparse-dst2.bin"; + + sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { + std::cout << "punched holes:\n"; + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096, 4096) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + + std::cout << 4096 << ".." << (4096 * 2) << std::endl; + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096 * 2, 4096) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + + std::cout << (4096 * 2) << ".." << (4096 * 3) << std::endl; + }); + + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); +} + +template +void sparse_copy_test(std::filesystem::path const &srcp, + std::filesystem::path const &dstp, + std::filesystem::path const &dst2p, + P &&punch_holes) { + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); + { - auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); - if (!map) { - std::cerr << "Failed to map a file" << std::endl; - return EXIT_FAILURE; + int src = ::open(srcp.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (src == -1) { + perror("open"); + std::exit(1); + } + + fill_file(src); + punch_holes(src); + close(src); + } + + { // copy using copy_file_sparse_linux + int src = ::open(srcp.c_str(), O_RDONLY); + if (src == -1) { + perror("open"); + std::exit(1); + } + + struct stat st; + fstat(src, &st); + + int dst = ::open(dstp.c_str(), O_CREAT | O_WRONLY | O_TRUNC, st.st_mode); + if (dst == -1) { + perror("open"); + std::exit(1); + } + + metall::mtlldetail::fcpdtl::copy_file_sparse_linux(src, dst, st.st_size); + close(src); + close(dst); + } + + { // copy using cp + std::stringstream cmd; + cmd << "cp --sparse=always " << srcp << " " << dst2p; + + int res = std::system(cmd.str().c_str()); + assert(WIFEXITED(res)); + } + + int src = ::open(srcp.c_str(), O_RDONLY); + if (src == -1) { + perror("open"); + std::exit(1); + } + + int dst = ::open(dstp.c_str(), O_RDONLY); + if (dst == -1) { + perror("open"); + std::exit(1); } - auto* buf = reinterpret_cast(map); - buf[0] = 1; - buf[1024 * 1024 - 1] = 1; - if (!mdtl::munmap(fd, map, size, true)) { - std::cerr << "Failed to unmap a file" << std::endl; - return EXIT_FAILURE; + + int dst2 = ::open(dst2p.c_str(), O_RDONLY); + if (dst2 == -1) { + perror("open"); + std::exit(1); + } + + auto holes_src = get_holes(src); + auto holes_dst = get_holes(dst); + auto holes_dst2 = get_holes(dst2); + + std::cout << "src holes:\n"; + list_holes(holes_src); + std::cout << std::endl; + + std::cout << "dst holes:\n"; + list_holes(holes_dst); + std::cout << std::endl; + + std::cout << "dst2 holes:\n"; + list_holes(holes_dst2); + std::cout << std::endl; + + std::cout << "comparing src, dst" << std::endl; + check_files_eq(src, dst); + check_holes_eq(holes_src, holes_dst); + + // Not comparing holes to what cp produced because + // it tries to find more holes in the file or extend existing ones + std::cout << "comparing dst, dst2" << std::endl; + check_files_eq(dst, dst2); + + std::cout << "comparing dst2, src" << std::endl; + check_files_eq(dst2, src); + + std::cout << std::endl; +} + +void fill_file(int fd) { + std::vector buf; + std::uniform_int_distribution dist{1, std::numeric_limits::max()}; + + for (size_t ix = 0; ix < FILE_SIZE; ++ix) { + buf.push_back(dist(rng)); + assert(buf.back() != 0); + } + + size_t bytes_written = write(fd, buf.data(), buf.size()); + assert(bytes_written == FILE_SIZE); + + struct stat st; + fstat(fd, &st); + assert(st.st_size == FILE_SIZE); + + auto [ptr, sz] = mmap(fd); + for (size_t ix = 0; ix < static_cast(sz); ++ix) { + assert(ptr[ix] != 0); + } +} + +void punch_holes(int fd, bool hole_at_start, bool hole_at_end) { + std::vector> holes; + + std::uniform_int_distribution hole_num_dist{static_cast(1 + hole_at_start + hole_at_end), 10}; + std::uniform_int_distribution hole_size_dist{1, FILE_SIZE/10}; + std::uniform_int_distribution hole_off_dist{0, FILE_SIZE - FILE_SIZE/10}; + + std::cout << "punched holes:\n"; + + auto num_holes = hole_num_dist(rng); + for (size_t ix = 0; ix < num_holes - hole_at_end - hole_at_end; ++ix) { + auto hole_start = hole_off_dist(rng); + auto hole_size = hole_size_dist(rng); + + holes.emplace_back(hole_start, hole_start + hole_size); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, hole_start, hole_size) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); } } - if (mdtl::get_file_size(src_path) < size) { - std::cerr << "Failed to extend file size" << std::endl; - return EXIT_FAILURE; + if (hole_at_start) { + auto sz = hole_size_dist(rng); + holes.emplace_back(0, sz); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, sz) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } } - if (mdtl::get_actual_file_size(src_path) >= size) { - std::cerr << "Failed to create a sparse file" << std::endl; - std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) - << std::endl; - return EXIT_FAILURE; + if (hole_at_end) { + auto sz = hole_size_dist(rng); + holes.emplace_back(FILE_SIZE - sz, FILE_SIZE); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, FILE_SIZE - sz, sz) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } } - // Sparse copy - const std::filesystem::path dst_path = "destination.dat"; - if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { - std::cerr << "Failed to copy a file" << std::endl; - return EXIT_FAILURE; + std::sort(holes.begin(), holes.end()); + for (auto const &[start, end] : holes) { + std::cout << start << ".." << end << std::endl; } - std::cout << "Source file size: " << mdtl::get_file_size(src_path) - << std::endl; - std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) - << std::endl; - - // Show actual size - std::cout << "Source actual file size: " - << mdtl::get_actual_file_size(src_path) << std::endl; - std::cout << "Destination actual file size: " - << mdtl::get_actual_file_size(dst_path) << std::endl; - - // if (mdtl::get_file_size(dst_path) < size) { - // std::cerr << "Failed to extend file size" << std::endl; - // return EXIT_FAILURE; - // } - -// if (mdtl::get_actual_file_size(dst_path) >= size) { -// std::cerr << "Failed to create a sparse file" << std::endl; -// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) -// << std::endl; -// return EXIT_FAILURE; -// } - - return 0; -} \ No newline at end of file + std::cout << std::endl; +} + +std::pair mmap(int fd) { + struct stat st; + int res = fstat(fd, &st); + assert(res >= 0); + + auto *ptr = ::mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + assert(ptr != MAP_FAILED); + + return {static_cast(ptr), st.st_size}; +} + +void check_files_eq(int a, int b) { + auto am = mmap(a); + auto a_ptr = am.first; + auto a_size = am.second; + + auto bm = mmap(b); + auto b_ptr = bm.first; + auto b_size = bm.second; + + assert(a_size == b_size); + + for (size_t ix = 0; ix < static_cast(a_size); ++ix) { + if (a_ptr[ix] != b_ptr[ix]) { + std::cerr << "found different bytes: " << std::hex << static_cast(a_ptr[ix]) << " vs " << static_cast(b_ptr[ix]) << std::endl; + assert(false); + } + } +} + +std::vector> get_holes(int fd) { + lseek(fd, 0, SEEK_SET); + std::vector> holes; + + off_t off = 0; + off_t hole_start = 0; + + while (true) { + hole_start = lseek(fd, off, SEEK_HOLE); + off_t const hole_end = lseek(fd, hole_start, SEEK_DATA); + + if (hole_end == -1) { + break; + } + + off = hole_end; + holes.emplace_back(hole_start, hole_end); + } + + struct stat st; + fstat(fd, &st); + + if (hole_start < st.st_size) { + holes.emplace_back(hole_start, st.st_size); + } + + return holes; +} + +void list_holes(std::vector> const &holes) { + for (auto const &hole : holes) { + std::cout << "hole: " << hole.first << ".." << hole.second << std::endl; + } +} + +void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b) { + assert(holes_a.size() == holes_b.size()); + + for (size_t ix = 0; ix < holes_a.size(); ++ix) { + if (holes_a[ix] != holes_b[ix]) { + std::cerr << "hole mismatch: " << holes_a[ix].first << ".." << holes_a[ix].second << " vs " << holes_b[ix].first << ".." << holes_b[ix].second << std::endl; + assert(false); + } + } +} diff --git a/verification/sparse_copy/verify_sparse_copy_manual.cpp b/verification/sparse_copy/verify_sparse_copy_manual.cpp new file mode 100644 index 00000000..d69e45f5 --- /dev/null +++ b/verification/sparse_copy/verify_sparse_copy_manual.cpp @@ -0,0 +1,87 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include +#include +#include + +#include +#include + +namespace mdtl = metall::mtlldetail; + +int main() { + const std::filesystem::path src_path = "source.dat"; + const ssize_t size = 1024 * 1024 * 32; + + if (!mdtl::create_file(src_path)) { + std::cerr << "Failed to create a file" << std::endl; + return EXIT_FAILURE; + } + + if (!mdtl::extend_file_size(src_path, size)) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + { + auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); + if (!map) { + std::cerr << "Failed to map a file" << std::endl; + return EXIT_FAILURE; + } + auto* buf = reinterpret_cast(map); + buf[0] = 1; + buf[1024 * 1024 - 1] = 1; + if (!mdtl::munmap(fd, map, size, true)) { + std::cerr << "Failed to unmap a file" << std::endl; + return EXIT_FAILURE; + } + } + + if (mdtl::get_file_size(src_path) < size) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; + } + + if (mdtl::get_actual_file_size(src_path) >= size) { + std::cerr << "Failed to create a sparse file" << std::endl; + std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) + << std::endl; + return EXIT_FAILURE; + } + + // Sparse copy + const std::filesystem::path dst_path = "destination.dat"; + if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { + std::cerr << "Failed to copy a file" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Source file size: " << mdtl::get_file_size(src_path) + << std::endl; + std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) + << std::endl; + + // Show actual size + std::cout << "Source actual file size: " + << mdtl::get_actual_file_size(src_path) << std::endl; + std::cout << "Destination actual file size: " + << mdtl::get_actual_file_size(dst_path) << std::endl; + + // if (mdtl::get_file_size(dst_path) < size) { + // std::cerr << "Failed to extend file size" << std::endl; + // return EXIT_FAILURE; + // } + +// if (mdtl::get_actual_file_size(dst_path) >= size) { +// std::cerr << "Failed to create a sparse file" << std::endl; +// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) +// << std::endl; +// return EXIT_FAILURE; +// } + + return 0; +} \ No newline at end of file From 757363327edb960fe689f3aa8820430f3415e88b Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:04:27 +0100 Subject: [PATCH 15/26] rename files to not confuse git --- verification/sparse_copy/CMakeLists.txt | 4 +- .../sparse_copy/verify_sparse_copy.cpp | 421 +++--------------- .../sparse_copy/verify_sparse_copy_manual.cpp | 87 ---- .../verify_sparse_copy_syscalls.cpp | 376 ++++++++++++++++ 4 files changed, 444 insertions(+), 444 deletions(-) delete mode 100644 verification/sparse_copy/verify_sparse_copy_manual.cpp create mode 100644 verification/sparse_copy/verify_sparse_copy_syscalls.cpp diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt index d1db9b43..e58dbbc0 100644 --- a/verification/sparse_copy/CMakeLists.txt +++ b/verification/sparse_copy/CMakeLists.txt @@ -1,2 +1,2 @@ -add_metall_executable(verify_sparse_copy_manual verify_sparse_copy_manual.cpp) -add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) \ No newline at end of file +add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) +add_metall_executable(verify_sparse_copy_syscalls verify_sparse_copy_syscalls.cpp) \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy.cpp b/verification/sparse_copy/verify_sparse_copy.cpp index 0811d68a..d69e45f5 100644 --- a/verification/sparse_copy/verify_sparse_copy.cpp +++ b/verification/sparse_copy/verify_sparse_copy.cpp @@ -1,376 +1,87 @@ -#undef NDEBUG +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) -#include +#include +#include +#include #include +#include -#include -#include - -std::filesystem::path make_test_path(); -void randomized_copy_file_sparse_linux(std::filesystem::path const &p); -void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p); +namespace mdtl = metall::mtlldetail; int main() { - auto p = make_test_path(); - - randomized_copy_file_sparse_linux(p); - adjacent_hole_copy_file_sparse_linux(p); -} - - -// Utility - -constexpr size_t FILE_SIZE = 4096*4*11; -std::default_random_engine rng{std::random_device{}()}; - -/** - * Fills file at fd with FILE_SIZE random bytes - */ -void fill_file(int fd); - -/** - * Randomly punches 1-9 holes into fd. - * - * @param hole_at_start forces a hole to be created at the start of the file - * @param hole_at_end forces a hole to be created at the end of the file - */ -void punch_holes(int fd, bool hole_at_start = false, bool hole_at_end = false); - -/** - * Convenience wrapper around ::mmap - * @param fd file descriptor to mmap - * @return pair (pointer to mapped range, size of mapped range) - */ -std::pair mmap(int fd); - -/** - * Checks if files a and b are equal byte by byte - */ -void check_files_eq(int a, int b); - -/** - * Returns a list of all holes in the given file - */ -std::vector> get_holes(int fd); - -/** - * print all given holes - */ - void list_holes(std::vector> const &holes); - -/** - * Checks if the holes in a and b are in the same places and of the same size - */ -void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b); + const std::filesystem::path src_path = "source.dat"; + const ssize_t size = 1024 * 1024 * 32; -/** - * Runs the test - */ -template -void sparse_copy_test(std::filesystem::path const &srcp, - std::filesystem::path const &dstp, - std::filesystem::path const &dst2p, - P &&punch_holes); - - -// Impl - -std::filesystem::path make_test_path() { - std::filesystem::path p{"/tmp/metallsparsecopytest" + std::to_string(rng())}; - std::filesystem::create_directory(p); - return p; -} - -void randomized_copy_file_sparse_linux(std::filesystem::path const &p) { - auto srcp = p / "copy_file_sparse-src.bin"; - auto dstp = p / "copy_file_sparse-dst.bin"; - auto dst2p = p / "copy_file_sparse-dst2.bin"; - - std::uniform_int_distribution b{0, 1}; - - for (size_t ix = 0; ix < 1000; ++ix) { - sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { - punch_holes(fd, b(rng), b(rng)); - }); + if (!mdtl::create_file(src_path)) { + std::cerr << "Failed to create a file" << std::endl; + return EXIT_FAILURE; } - std::filesystem::remove(srcp); - std::filesystem::remove(dstp); - std::filesystem::remove(dst2p); -} - -void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p) { - auto srcp = p / "adj-copy_file_sparse-src.bin"; - auto dstp = p / "adj-copy_file_sparse-dst.bin"; - auto dst2p = p / "adj-copy_file_sparse-dst2.bin"; - - sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { - std::cout << "punched holes:\n"; - - if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096, 4096) == -1) { - perror("fallocate (punch_holes)"); - std::exit(1); - } - - std::cout << 4096 << ".." << (4096 * 2) << std::endl; - - if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096 * 2, 4096) == -1) { - perror("fallocate (punch_holes)"); - std::exit(1); - } - - std::cout << (4096 * 2) << ".." << (4096 * 3) << std::endl; - }); - - std::filesystem::remove(srcp); - std::filesystem::remove(dstp); - std::filesystem::remove(dst2p); -} - -template -void sparse_copy_test(std::filesystem::path const &srcp, - std::filesystem::path const &dstp, - std::filesystem::path const &dst2p, - P &&punch_holes) { - std::filesystem::remove(srcp); - std::filesystem::remove(dstp); - std::filesystem::remove(dst2p); - - { - int src = ::open(srcp.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (src == -1) { - perror("open"); - std::exit(1); - } - - fill_file(src); - punch_holes(src); - close(src); - } - - { // copy using copy_file_sparse_linux - int src = ::open(srcp.c_str(), O_RDONLY); - if (src == -1) { - perror("open"); - std::exit(1); - } - - struct stat st; - fstat(src, &st); - - int dst = ::open(dstp.c_str(), O_CREAT | O_WRONLY | O_TRUNC, st.st_mode); - if (dst == -1) { - perror("open"); - std::exit(1); - } - - metall::mtlldetail::fcpdtl::copy_file_sparse_linux(src, dst, st.st_size); - close(src); - close(dst); - } - - { // copy using cp - std::stringstream cmd; - cmd << "cp --sparse=always " << srcp << " " << dst2p; - - int res = std::system(cmd.str().c_str()); - assert(WIFEXITED(res)); - } - - int src = ::open(srcp.c_str(), O_RDONLY); - if (src == -1) { - perror("open"); - std::exit(1); - } - - int dst = ::open(dstp.c_str(), O_RDONLY); - if (dst == -1) { - perror("open"); - std::exit(1); - } - - int dst2 = ::open(dst2p.c_str(), O_RDONLY); - if (dst2 == -1) { - perror("open"); - std::exit(1); - } - - auto holes_src = get_holes(src); - auto holes_dst = get_holes(dst); - auto holes_dst2 = get_holes(dst2); - - std::cout << "src holes:\n"; - list_holes(holes_src); - std::cout << std::endl; - - std::cout << "dst holes:\n"; - list_holes(holes_dst); - std::cout << std::endl; - - std::cout << "dst2 holes:\n"; - list_holes(holes_dst2); - std::cout << std::endl; - - std::cout << "comparing src, dst" << std::endl; - check_files_eq(src, dst); - check_holes_eq(holes_src, holes_dst); - - // Not comparing holes to what cp produced because - // it tries to find more holes in the file or extend existing ones - std::cout << "comparing dst, dst2" << std::endl; - check_files_eq(dst, dst2); - - std::cout << "comparing dst2, src" << std::endl; - check_files_eq(dst2, src); - - std::cout << std::endl; -} - -void fill_file(int fd) { - std::vector buf; - std::uniform_int_distribution dist{1, std::numeric_limits::max()}; - - for (size_t ix = 0; ix < FILE_SIZE; ++ix) { - buf.push_back(dist(rng)); - assert(buf.back() != 0); - } - - size_t bytes_written = write(fd, buf.data(), buf.size()); - assert(bytes_written == FILE_SIZE); - - struct stat st; - fstat(fd, &st); - assert(st.st_size == FILE_SIZE); - - auto [ptr, sz] = mmap(fd); - for (size_t ix = 0; ix < static_cast(sz); ++ix) { - assert(ptr[ix] != 0); - } -} - -void punch_holes(int fd, bool hole_at_start, bool hole_at_end) { - std::vector> holes; - - std::uniform_int_distribution hole_num_dist{static_cast(1 + hole_at_start + hole_at_end), 10}; - std::uniform_int_distribution hole_size_dist{1, FILE_SIZE/10}; - std::uniform_int_distribution hole_off_dist{0, FILE_SIZE - FILE_SIZE/10}; - - std::cout << "punched holes:\n"; - - auto num_holes = hole_num_dist(rng); - for (size_t ix = 0; ix < num_holes - hole_at_end - hole_at_end; ++ix) { - auto hole_start = hole_off_dist(rng); - auto hole_size = hole_size_dist(rng); - - holes.emplace_back(hole_start, hole_start + hole_size); - - if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, hole_start, hole_size) == -1) { - perror("fallocate (punch_holes)"); - std::exit(1); - } - } - - if (hole_at_start) { - auto sz = hole_size_dist(rng); - holes.emplace_back(0, sz); - - if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, sz) == -1) { - perror("fallocate (punch_holes)"); - std::exit(1); - } + if (!mdtl::extend_file_size(src_path, size)) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; } - if (hole_at_end) { - auto sz = hole_size_dist(rng); - holes.emplace_back(FILE_SIZE - sz, FILE_SIZE); - - if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, FILE_SIZE - sz, sz) == -1) { - perror("fallocate (punch_holes)"); - std::exit(1); + { + auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); + if (!map) { + std::cerr << "Failed to map a file" << std::endl; + return EXIT_FAILURE; } - } - - std::sort(holes.begin(), holes.end()); - for (auto const &[start, end] : holes) { - std::cout << start << ".." << end << std::endl; - } - - std::cout << std::endl; -} - -std::pair mmap(int fd) { - struct stat st; - int res = fstat(fd, &st); - assert(res >= 0); - - auto *ptr = ::mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - assert(ptr != MAP_FAILED); - - return {static_cast(ptr), st.st_size}; -} - -void check_files_eq(int a, int b) { - auto am = mmap(a); - auto a_ptr = am.first; - auto a_size = am.second; - - auto bm = mmap(b); - auto b_ptr = bm.first; - auto b_size = bm.second; - - assert(a_size == b_size); - - for (size_t ix = 0; ix < static_cast(a_size); ++ix) { - if (a_ptr[ix] != b_ptr[ix]) { - std::cerr << "found different bytes: " << std::hex << static_cast(a_ptr[ix]) << " vs " << static_cast(b_ptr[ix]) << std::endl; - assert(false); + auto* buf = reinterpret_cast(map); + buf[0] = 1; + buf[1024 * 1024 - 1] = 1; + if (!mdtl::munmap(fd, map, size, true)) { + std::cerr << "Failed to unmap a file" << std::endl; + return EXIT_FAILURE; } } -} - -std::vector> get_holes(int fd) { - lseek(fd, 0, SEEK_SET); - std::vector> holes; - - off_t off = 0; - off_t hole_start = 0; - - while (true) { - hole_start = lseek(fd, off, SEEK_HOLE); - off_t const hole_end = lseek(fd, hole_start, SEEK_DATA); - - if (hole_end == -1) { - break; - } - off = hole_end; - holes.emplace_back(hole_start, hole_end); + if (mdtl::get_file_size(src_path) < size) { + std::cerr << "Failed to extend file size" << std::endl; + return EXIT_FAILURE; } - struct stat st; - fstat(fd, &st); - - if (hole_start < st.st_size) { - holes.emplace_back(hole_start, st.st_size); + if (mdtl::get_actual_file_size(src_path) >= size) { + std::cerr << "Failed to create a sparse file" << std::endl; + std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) + << std::endl; + return EXIT_FAILURE; } - return holes; -} - -void list_holes(std::vector> const &holes) { - for (auto const &hole : holes) { - std::cout << "hole: " << hole.first << ".." << hole.second << std::endl; + // Sparse copy + const std::filesystem::path dst_path = "destination.dat"; + if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { + std::cerr << "Failed to copy a file" << std::endl; + return EXIT_FAILURE; } -} - -void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b) { - assert(holes_a.size() == holes_b.size()); - for (size_t ix = 0; ix < holes_a.size(); ++ix) { - if (holes_a[ix] != holes_b[ix]) { - std::cerr << "hole mismatch: " << holes_a[ix].first << ".." << holes_a[ix].second << " vs " << holes_b[ix].first << ".." << holes_b[ix].second << std::endl; - assert(false); - } - } -} + std::cout << "Source file size: " << mdtl::get_file_size(src_path) + << std::endl; + std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) + << std::endl; + + // Show actual size + std::cout << "Source actual file size: " + << mdtl::get_actual_file_size(src_path) << std::endl; + std::cout << "Destination actual file size: " + << mdtl::get_actual_file_size(dst_path) << std::endl; + + // if (mdtl::get_file_size(dst_path) < size) { + // std::cerr << "Failed to extend file size" << std::endl; + // return EXIT_FAILURE; + // } + +// if (mdtl::get_actual_file_size(dst_path) >= size) { +// std::cerr << "Failed to create a sparse file" << std::endl; +// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) +// << std::endl; +// return EXIT_FAILURE; +// } + + return 0; +} \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy_manual.cpp b/verification/sparse_copy/verify_sparse_copy_manual.cpp deleted file mode 100644 index d69e45f5..00000000 --- a/verification/sparse_copy/verify_sparse_copy_manual.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall -// Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -#include -#include -#include - -#include -#include - -namespace mdtl = metall::mtlldetail; - -int main() { - const std::filesystem::path src_path = "source.dat"; - const ssize_t size = 1024 * 1024 * 32; - - if (!mdtl::create_file(src_path)) { - std::cerr << "Failed to create a file" << std::endl; - return EXIT_FAILURE; - } - - if (!mdtl::extend_file_size(src_path, size)) { - std::cerr << "Failed to extend file size" << std::endl; - return EXIT_FAILURE; - } - - { - auto [fd, map] = mdtl::map_file_write_mode(src_path, nullptr, size, 0); - if (!map) { - std::cerr << "Failed to map a file" << std::endl; - return EXIT_FAILURE; - } - auto* buf = reinterpret_cast(map); - buf[0] = 1; - buf[1024 * 1024 - 1] = 1; - if (!mdtl::munmap(fd, map, size, true)) { - std::cerr << "Failed to unmap a file" << std::endl; - return EXIT_FAILURE; - } - } - - if (mdtl::get_file_size(src_path) < size) { - std::cerr << "Failed to extend file size" << std::endl; - return EXIT_FAILURE; - } - - if (mdtl::get_actual_file_size(src_path) >= size) { - std::cerr << "Failed to create a sparse file" << std::endl; - std::cerr << "Actual file size: " << mdtl::get_actual_file_size(src_path) - << std::endl; - return EXIT_FAILURE; - } - - // Sparse copy - const std::filesystem::path dst_path = "destination.dat"; - if (!mdtl::fcpdtl::copy_file_sparse_manually(src_path, dst_path)) { - std::cerr << "Failed to copy a file" << std::endl; - return EXIT_FAILURE; - } - - std::cout << "Source file size: " << mdtl::get_file_size(src_path) - << std::endl; - std::cout << "Destination file size: " << mdtl::get_file_size(dst_path) - << std::endl; - - // Show actual size - std::cout << "Source actual file size: " - << mdtl::get_actual_file_size(src_path) << std::endl; - std::cout << "Destination actual file size: " - << mdtl::get_actual_file_size(dst_path) << std::endl; - - // if (mdtl::get_file_size(dst_path) < size) { - // std::cerr << "Failed to extend file size" << std::endl; - // return EXIT_FAILURE; - // } - -// if (mdtl::get_actual_file_size(dst_path) >= size) { -// std::cerr << "Failed to create a sparse file" << std::endl; -// std::cerr << "Actual file size: " << mdtl::get_actual_file_size(dst_path) -// << std::endl; -// return EXIT_FAILURE; -// } - - return 0; -} \ No newline at end of file diff --git a/verification/sparse_copy/verify_sparse_copy_syscalls.cpp b/verification/sparse_copy/verify_sparse_copy_syscalls.cpp new file mode 100644 index 00000000..0811d68a --- /dev/null +++ b/verification/sparse_copy/verify_sparse_copy_syscalls.cpp @@ -0,0 +1,376 @@ +#undef NDEBUG + +#include + +#include + +#include +#include + +std::filesystem::path make_test_path(); +void randomized_copy_file_sparse_linux(std::filesystem::path const &p); +void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p); + +int main() { + auto p = make_test_path(); + + randomized_copy_file_sparse_linux(p); + adjacent_hole_copy_file_sparse_linux(p); +} + + +// Utility + +constexpr size_t FILE_SIZE = 4096*4*11; +std::default_random_engine rng{std::random_device{}()}; + +/** + * Fills file at fd with FILE_SIZE random bytes + */ +void fill_file(int fd); + +/** + * Randomly punches 1-9 holes into fd. + * + * @param hole_at_start forces a hole to be created at the start of the file + * @param hole_at_end forces a hole to be created at the end of the file + */ +void punch_holes(int fd, bool hole_at_start = false, bool hole_at_end = false); + +/** + * Convenience wrapper around ::mmap + * @param fd file descriptor to mmap + * @return pair (pointer to mapped range, size of mapped range) + */ +std::pair mmap(int fd); + +/** + * Checks if files a and b are equal byte by byte + */ +void check_files_eq(int a, int b); + +/** + * Returns a list of all holes in the given file + */ +std::vector> get_holes(int fd); + +/** + * print all given holes + */ + void list_holes(std::vector> const &holes); + +/** + * Checks if the holes in a and b are in the same places and of the same size + */ +void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b); + +/** + * Runs the test + */ +template +void sparse_copy_test(std::filesystem::path const &srcp, + std::filesystem::path const &dstp, + std::filesystem::path const &dst2p, + P &&punch_holes); + + +// Impl + +std::filesystem::path make_test_path() { + std::filesystem::path p{"/tmp/metallsparsecopytest" + std::to_string(rng())}; + std::filesystem::create_directory(p); + return p; +} + +void randomized_copy_file_sparse_linux(std::filesystem::path const &p) { + auto srcp = p / "copy_file_sparse-src.bin"; + auto dstp = p / "copy_file_sparse-dst.bin"; + auto dst2p = p / "copy_file_sparse-dst2.bin"; + + std::uniform_int_distribution b{0, 1}; + + for (size_t ix = 0; ix < 1000; ++ix) { + sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { + punch_holes(fd, b(rng), b(rng)); + }); + } + + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); +} + +void adjacent_hole_copy_file_sparse_linux(std::filesystem::path const &p) { + auto srcp = p / "adj-copy_file_sparse-src.bin"; + auto dstp = p / "adj-copy_file_sparse-dst.bin"; + auto dst2p = p / "adj-copy_file_sparse-dst2.bin"; + + sparse_copy_test(srcp, dstp, dst2p, [&](int fd) { + std::cout << "punched holes:\n"; + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096, 4096) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + + std::cout << 4096 << ".." << (4096 * 2) << std::endl; + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 4096 * 2, 4096) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + + std::cout << (4096 * 2) << ".." << (4096 * 3) << std::endl; + }); + + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); +} + +template +void sparse_copy_test(std::filesystem::path const &srcp, + std::filesystem::path const &dstp, + std::filesystem::path const &dst2p, + P &&punch_holes) { + std::filesystem::remove(srcp); + std::filesystem::remove(dstp); + std::filesystem::remove(dst2p); + + { + int src = ::open(srcp.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (src == -1) { + perror("open"); + std::exit(1); + } + + fill_file(src); + punch_holes(src); + close(src); + } + + { // copy using copy_file_sparse_linux + int src = ::open(srcp.c_str(), O_RDONLY); + if (src == -1) { + perror("open"); + std::exit(1); + } + + struct stat st; + fstat(src, &st); + + int dst = ::open(dstp.c_str(), O_CREAT | O_WRONLY | O_TRUNC, st.st_mode); + if (dst == -1) { + perror("open"); + std::exit(1); + } + + metall::mtlldetail::fcpdtl::copy_file_sparse_linux(src, dst, st.st_size); + close(src); + close(dst); + } + + { // copy using cp + std::stringstream cmd; + cmd << "cp --sparse=always " << srcp << " " << dst2p; + + int res = std::system(cmd.str().c_str()); + assert(WIFEXITED(res)); + } + + int src = ::open(srcp.c_str(), O_RDONLY); + if (src == -1) { + perror("open"); + std::exit(1); + } + + int dst = ::open(dstp.c_str(), O_RDONLY); + if (dst == -1) { + perror("open"); + std::exit(1); + } + + int dst2 = ::open(dst2p.c_str(), O_RDONLY); + if (dst2 == -1) { + perror("open"); + std::exit(1); + } + + auto holes_src = get_holes(src); + auto holes_dst = get_holes(dst); + auto holes_dst2 = get_holes(dst2); + + std::cout << "src holes:\n"; + list_holes(holes_src); + std::cout << std::endl; + + std::cout << "dst holes:\n"; + list_holes(holes_dst); + std::cout << std::endl; + + std::cout << "dst2 holes:\n"; + list_holes(holes_dst2); + std::cout << std::endl; + + std::cout << "comparing src, dst" << std::endl; + check_files_eq(src, dst); + check_holes_eq(holes_src, holes_dst); + + // Not comparing holes to what cp produced because + // it tries to find more holes in the file or extend existing ones + std::cout << "comparing dst, dst2" << std::endl; + check_files_eq(dst, dst2); + + std::cout << "comparing dst2, src" << std::endl; + check_files_eq(dst2, src); + + std::cout << std::endl; +} + +void fill_file(int fd) { + std::vector buf; + std::uniform_int_distribution dist{1, std::numeric_limits::max()}; + + for (size_t ix = 0; ix < FILE_SIZE; ++ix) { + buf.push_back(dist(rng)); + assert(buf.back() != 0); + } + + size_t bytes_written = write(fd, buf.data(), buf.size()); + assert(bytes_written == FILE_SIZE); + + struct stat st; + fstat(fd, &st); + assert(st.st_size == FILE_SIZE); + + auto [ptr, sz] = mmap(fd); + for (size_t ix = 0; ix < static_cast(sz); ++ix) { + assert(ptr[ix] != 0); + } +} + +void punch_holes(int fd, bool hole_at_start, bool hole_at_end) { + std::vector> holes; + + std::uniform_int_distribution hole_num_dist{static_cast(1 + hole_at_start + hole_at_end), 10}; + std::uniform_int_distribution hole_size_dist{1, FILE_SIZE/10}; + std::uniform_int_distribution hole_off_dist{0, FILE_SIZE - FILE_SIZE/10}; + + std::cout << "punched holes:\n"; + + auto num_holes = hole_num_dist(rng); + for (size_t ix = 0; ix < num_holes - hole_at_end - hole_at_end; ++ix) { + auto hole_start = hole_off_dist(rng); + auto hole_size = hole_size_dist(rng); + + holes.emplace_back(hole_start, hole_start + hole_size); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, hole_start, hole_size) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + } + + if (hole_at_start) { + auto sz = hole_size_dist(rng); + holes.emplace_back(0, sz); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, sz) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + } + + if (hole_at_end) { + auto sz = hole_size_dist(rng); + holes.emplace_back(FILE_SIZE - sz, FILE_SIZE); + + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, FILE_SIZE - sz, sz) == -1) { + perror("fallocate (punch_holes)"); + std::exit(1); + } + } + + std::sort(holes.begin(), holes.end()); + for (auto const &[start, end] : holes) { + std::cout << start << ".." << end << std::endl; + } + + std::cout << std::endl; +} + +std::pair mmap(int fd) { + struct stat st; + int res = fstat(fd, &st); + assert(res >= 0); + + auto *ptr = ::mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + assert(ptr != MAP_FAILED); + + return {static_cast(ptr), st.st_size}; +} + +void check_files_eq(int a, int b) { + auto am = mmap(a); + auto a_ptr = am.first; + auto a_size = am.second; + + auto bm = mmap(b); + auto b_ptr = bm.first; + auto b_size = bm.second; + + assert(a_size == b_size); + + for (size_t ix = 0; ix < static_cast(a_size); ++ix) { + if (a_ptr[ix] != b_ptr[ix]) { + std::cerr << "found different bytes: " << std::hex << static_cast(a_ptr[ix]) << " vs " << static_cast(b_ptr[ix]) << std::endl; + assert(false); + } + } +} + +std::vector> get_holes(int fd) { + lseek(fd, 0, SEEK_SET); + std::vector> holes; + + off_t off = 0; + off_t hole_start = 0; + + while (true) { + hole_start = lseek(fd, off, SEEK_HOLE); + off_t const hole_end = lseek(fd, hole_start, SEEK_DATA); + + if (hole_end == -1) { + break; + } + + off = hole_end; + holes.emplace_back(hole_start, hole_end); + } + + struct stat st; + fstat(fd, &st); + + if (hole_start < st.st_size) { + holes.emplace_back(hole_start, st.st_size); + } + + return holes; +} + +void list_holes(std::vector> const &holes) { + for (auto const &hole : holes) { + std::cout << "hole: " << hole.first << ".." << hole.second << std::endl; + } +} + +void check_holes_eq(std::vector> const &holes_a, std::vector> const &holes_b) { + assert(holes_a.size() == holes_b.size()); + + for (size_t ix = 0; ix < holes_a.size(); ++ix) { + if (holes_a[ix] != holes_b[ix]) { + std::cerr << "hole mismatch: " << holes_a[ix].first << ".." << holes_a[ix].second << " vs " << holes_b[ix].first << ".." << holes_b[ix].second << std::endl; + assert(false); + } + } +} From de4d48a6e3eba78713fb1353e8ab433dafa86eac Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:28:53 +0100 Subject: [PATCH 16/26] add ability to implement custom loggers --- example/CMakeLists.txt | 3 + example/custom_logger.cpp | 33 +++++ example/logger.cpp | 2 +- include/metall/detail/soft_dirty_page.hpp | 1 + include/metall/logger.hpp | 163 ++++++++++++++-------- include/metall/logger_interface.h | 37 +++++ test/container/stl_allocator_test.cpp | 4 +- test/kernel/manager_test.cpp | 2 +- verification/logger/verify_logger.cpp | 16 +-- 9 files changed, 191 insertions(+), 70 deletions(-) create mode 100644 example/custom_logger.cpp create mode 100644 include/metall/logger_interface.h diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 55997163..419934d0 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -38,6 +38,9 @@ add_metall_executable(logger logger.cpp) add_metall_executable(concurrent concurrent.cpp) +add_metall_executable(custom_logger custom_logger.cpp) +target_compile_definitions(custom_logger PRIVATE METALL_LOGGER_EXTERN_C=1) + if (BUILD_C) add_c_executable(c_api c_api.c) target_link_libraries(c_api PRIVATE metall_c) diff --git a/example/custom_logger.cpp b/example/custom_logger.cpp new file mode 100644 index 00000000..9273e4a5 --- /dev/null +++ b/example/custom_logger.cpp @@ -0,0 +1,33 @@ +// Copyright 2023 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include + +/// \brief converts the given level into string literal +const char *log_lvl_to_string(metall_log_level lvl) { + switch (lvl) { + case metall_critical: return "CRIT"; + case metall_error: return "ERROR"; + case metall_warning: return "WARN"; + case metall_info: return "INFO"; + case metall_debug: return "DEBUG"; + case metall_verbose: return "VERBOSE"; + default: return "UNKNOWN"; + } +} + +/// \brief custom logger implementation +/// Output looks different than default. +extern "C" void metall_log(metall_log_level lvl, const char *file, size_t line_no, const char *message) { + std::cerr << log_lvl_to_string(lvl) << " metall{file=" << file + << ", line=" << line_no << "}: " << message << std::endl; +} + +int main() { + // Do Metall operations + metall::manager manager(metall::create_only, "/tmp/dir"); + + return 0; +} diff --git a/example/logger.cpp b/example/logger.cpp index efc98653..e86d7bf6 100644 --- a/example/logger.cpp +++ b/example/logger.cpp @@ -8,7 +8,7 @@ int main() { // Set the log level to , for example, verbose. // The log level can be changed at any time. - metall::logger::set_log_level(metall::logger::level::verbose); + metall::logger::set_log_level(metall::logger::level_filter::verbose); // Enable the program to abort when a critical log message is displayed. metall::logger::abort_on_critical_error(true); diff --git a/include/metall/detail/soft_dirty_page.hpp b/include/metall/detail/soft_dirty_page.hpp index d8274510..df18a484 100644 --- a/include/metall/detail/soft_dirty_page.hpp +++ b/include/metall/detail/soft_dirty_page.hpp @@ -6,6 +6,7 @@ #ifndef METALL_DETAIL_UTILITY_SOFT_DIRTY_PAGE_HPP #define METALL_DETAIL_UTILITY_SOFT_DIRTY_PAGE_HPP +#include #include #include #include diff --git a/include/metall/logger.hpp b/include/metall/logger.hpp index 9934c617..f1f4af9b 100644 --- a/include/metall/logger.hpp +++ b/include/metall/logger.hpp @@ -6,10 +6,10 @@ #ifndef METALL_LOGGER_HPP #define METALL_LOGGER_HPP -#include -#include -#include -#include +#include +#include +#include +#include namespace metall { @@ -17,70 +17,34 @@ class logger { public: /// \brief Log message level enum struct level { - /// \brief Silent logger message — never show logger message - silent = 10, - /// \brief Critical logger message — abort the execution unless disabled - critical = 5, + /// \brief Critical logger message — with default logger implementation abort the execution unless disabled + critical = metall_critical, /// \brief Error logger message - error = 4, + error = metall_error, /// \brief Warning logger message - warning = 3, + warning = metall_warning, /// \brief Info logger message - info = 2, + info = metall_info, /// \brief Debug logger message - debug = 1, + debug = metall_debug, /// \brief Verbose (lowest priority) logger message - verbose = 0, + verbose = metall_verbose, }; - /// \brief Set the minimum logger level to show message - static void set_log_level(const level lvl) noexcept { - log_message_out_level = lvl; - } - - /// \brief If true is specified, enable an abort at a critical logger message - static void abort_on_critical_error(const bool enable) noexcept { - abort_on_critical = enable; - } - - /// \brief Log a message to std::cerr if the specified logger level is equal - /// to or higher than the pre-set logger level. + /// \brief Log a message static void out(const level lvl, const char* const file_name, const int line_no, const char* const message) noexcept { - if (log_message_out_level == level::silent || lvl == level::silent || - lvl < log_message_out_level) - return; - - try { - std::cerr << file_name << " at line " << line_no << " --- " << message - << std::endl; - } catch (...) { - } - - if (lvl == level::critical && abort_on_critical) { - std::abort(); - } + metall_log(static_cast(lvl), file_name, line_no, message); } - /// \brief Log a message about errno if the specified logger level is equal to - /// or higher than the pre-set logger level. + /// \brief Log a message about errno static void perror(const level lvl, const char* const file_name, const int line_no, const char* const message) noexcept { - if (log_message_out_level == level::silent || lvl == level::silent || - lvl < log_message_out_level) - return; - - try { - std::cerr << file_name << " at line " << line_no << " --- "; - std::perror(message); - } catch (...) { - } - - // std::out << "errno is " << errno << std::endl; + std::stringstream ss; + ss << message << ": " << strerror(errno); - if (lvl == level::critical && abort_on_critical) { - std::abort(); - } + auto const m = ss.str(); + metall_log(static_cast(lvl), file_name, line_no, m.c_str()); } logger() = delete; @@ -90,14 +54,99 @@ class logger { logger& operator=(const logger&) = delete; logger& operator=(logger&&) = delete; - private: - static level log_message_out_level; +#ifndef METALL_LOGGER_EXTERN_C + /// \brief determines the minimum level of messages that should be loggged + struct level_filter { + private: + std::optional inner; + + explicit level_filter(std::optional inner) noexcept : inner{inner} { + } + + public: + /// \brief Only log critical messages + static const level_filter critical; + /// \brief Only log error and critical messages + static const level_filter error; + /// \brief Only log warning, error and critical messages + static const level_filter warning; + /// \brief Only log info, warning, error and critical messages + static const level_filter info; + /// \brief Only log debug, info, warning, error and critical messages + static const level_filter debug; + /// \brief Log all messages + static const level_filter verbose; + /// \brief Don't log any messages + static const level_filter silent; + + /// \brief returns true if the logger should log a message of level lvl with this level_filter + bool should_log(level lvl) const noexcept { + return inner.has_value() && lvl >= *inner; + } + }; + + /// \return the current minimum logger level + static level_filter log_level() noexcept { + return log_message_out_level; + } + + /// \brief Set the minimum logger level to show message + static void set_log_level(const level_filter lvl) noexcept { + log_message_out_level = lvl; + } + + /// \return if the program should abort when a critical message is logged + static bool abort_on_critical_error() noexcept { + return abort_on_critical; + } + + /// \brief If true is specified, enable an abort at a critical logger message + static void abort_on_critical_error(const bool enable) noexcept { + abort_on_critical = enable; + } + +private: + static level_filter log_message_out_level; static bool abort_on_critical; +#endif // METALL_LOGGER_EXTERN_C }; -inline logger::level logger::log_message_out_level = logger::level::error; +#ifndef METALL_LOGGER_EXTERN_C +inline const logger::level_filter logger::level_filter::critical{logger::level::critical}; +inline const logger::level_filter logger::level_filter::error{logger::level::error}; +inline const logger::level_filter logger::level_filter::warning{logger::level::warning}; +inline const logger::level_filter logger::level_filter::info{logger::level::info}; +inline const logger::level_filter logger::level_filter::debug{logger::level::debug}; +inline const logger::level_filter logger::level_filter::verbose{logger::level::verbose}; +inline const logger::level_filter logger::level_filter::silent{std::nullopt}; + +inline logger::level_filter logger::log_message_out_level = logger::level_filter::error; inline bool logger::abort_on_critical = true; +#endif // METALL_LOGGER_EXTERN_C } // namespace metall + +#ifndef METALL_LOGGER_EXTERN_C +#include + +extern "C" inline void metall_log(metall_log_level lvl_, const char *file_name, size_t line_no, const char *message) { + auto const lvl = static_cast(lvl_); + + if (!metall::logger::log_level().should_log(lvl)) { + return; + } + + try { + std::cerr << file_name << " at line " << line_no << " --- " << message + << std::endl; + } catch (...) { + } + + if (lvl == metall::logger::level::critical && metall::logger::abort_on_critical_error()) { + std::abort(); + } +} +#endif // METALL_LOGGER_EXTERN_C + #endif // METALL_LOGGER_HPP diff --git a/include/metall/logger_interface.h b/include/metall/logger_interface.h new file mode 100644 index 00000000..ebc773a4 --- /dev/null +++ b/include/metall/logger_interface.h @@ -0,0 +1,37 @@ +#ifndef METALL_LOGGER_INTERFACE_H +#define METALL_LOGGER_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/// \brief Log message level +typedef enum metall_log_level { + /// \brief Critical logger message + metall_critical = 5, + /// \brief Error logger message + metall_error = 4, + /// \brief Warning logger message + metall_warning = 3, + /// \brief Info logger message + metall_info = 2, + /// \brief Debug logger message + metall_debug = 1, + /// \brief Verbose (lowest priority) logger message + metall_verbose = 0, +} metall_log_level; + + +/// \brief Implementation of logging behaviour. +/// +/// If METALL_LOGGER_EXTERN_C is defined this function does not have an implementation +/// and is intended to be implemented by consuming applications. +/// +/// If it is not defined the default implementation is in metall/logger.hpp. +void metall_log(metall_log_level lvl, const char *file_name, size_t line_no, const char *message); + +#ifdef __cplusplus +} +#endif + +#endif // METALL_LOGGER_INTERFACE_H diff --git a/test/container/stl_allocator_test.cpp b/test/container/stl_allocator_test.cpp index ebce177a..7a1644a1 100644 --- a/test/container/stl_allocator_test.cpp +++ b/test/container/stl_allocator_test.cpp @@ -141,7 +141,7 @@ TEST(StlAllocatorTest, Exception) { // Turn off log temporary because the following exception test cases could // show error messages - metall::logger::set_log_level(metall::logger::level::critical); + metall::logger::set_log_level(metall::logger::level_filter::critical); ASSERT_NO_THROW({ allocator.deallocate(allocator.allocate(1), 1); }); @@ -149,7 +149,7 @@ TEST(StlAllocatorTest, Exception) { { allocator.allocate(allocator.max_size() + 1); }, std::bad_array_new_length); - metall::logger::set_log_level(metall::logger::level::error); + metall::logger::set_log_level(metall::logger::level_filter::error); } TEST(StlAllocatorTest, Container) { diff --git a/test/kernel/manager_test.cpp b/test/kernel/manager_test.cpp index 7c728338..95bf322d 100644 --- a/test/kernel/manager_test.cpp +++ b/test/kernel/manager_test.cpp @@ -1325,7 +1325,7 @@ TEST(ManagerTest, Description) { TEST(ManagerTest, CheckSanity) { // This test should be run at the end of this execution unless reset the // values below: - metall::logger::set_log_level(metall::logger::level::silent); + metall::logger::set_log_level(metall::logger::level_filter::silent); metall::logger::abort_on_critical_error(false); { diff --git a/verification/logger/verify_logger.cpp b/verification/logger/verify_logger.cpp index e8a4ba28..56b380d9 100644 --- a/verification/logger/verify_logger.cpp +++ b/verification/logger/verify_logger.cpp @@ -9,7 +9,6 @@ using namespace metall; void log_cerr() { - logger::out(logger::level::silent, __FILE__, __LINE__, "silent logger"); logger::out(logger::level::critical, __FILE__, __LINE__, "critical logger"); logger::out(logger::level::error, __FILE__, __LINE__, "error logger"); logger::out(logger::level::warning, __FILE__, __LINE__, "warning logger"); @@ -19,7 +18,6 @@ void log_cerr() { } void log_perror() { - logger::perror(logger::level::silent, __FILE__, __LINE__, "silent logger"); logger::perror(logger::level::critical, __FILE__, __LINE__, "critical logger"); logger::perror(logger::level::error, __FILE__, __LINE__, "error logger"); @@ -37,37 +35,37 @@ int main() { log_perror(); std::cerr << "\n--- Log level : silent ---" << std::endl; - logger::set_log_level(logger::level::silent); + logger::set_log_level(logger::level_filter::silent); log_cerr(); log_perror(); std::cerr << "\n--- Log level : critical ---" << std::endl; - logger::set_log_level(logger::level::critical); + logger::set_log_level(logger::level_filter::critical); log_cerr(); log_perror(); std::cerr << "\n--- Log level : error ---" << std::endl; - logger::set_log_level(logger::level::error); + logger::set_log_level(logger::level_filter::error); log_cerr(); log_perror(); std::cerr << "\n--- Log level : warning ---" << std::endl; - logger::set_log_level(logger::level::warning); + logger::set_log_level(logger::level_filter::warning); log_cerr(); log_perror(); std::cerr << "\n--- Log level : info ---" << std::endl; - logger::set_log_level(logger::level::info); + logger::set_log_level(logger::level_filter::info); log_cerr(); log_perror(); std::cerr << "\n--- Log level : debug ---" << std::endl; - logger::set_log_level(logger::level::debug); + logger::set_log_level(logger::level_filter::debug); log_cerr(); log_perror(); std::cerr << "\n--- Log level : verbose ---" << std::endl; - logger::set_log_level(logger::level::verbose); + logger::set_log_level(logger::level_filter::verbose); log_cerr(); log_perror(); From 85fdc459c949a35d22e609da08c32b339c750f5f Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:43:47 +0100 Subject: [PATCH 17/26] use backslash for comments --- include/metall/detail/file.hpp | 18 +++++++++--------- .../verify_sparse_copy_syscalls.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/metall/detail/file.hpp b/include/metall/detail/file.hpp index 94675430..bdd92490 100644 --- a/include/metall/detail/file.hpp +++ b/include/metall/detail/file.hpp @@ -426,11 +426,11 @@ inline off_t prepare_file_copy_linux(const std::filesystem::path &source_path, } /** - * @brief Performs an accelerated, in-kernel copy from src to dst - * @param src source file descriptor - * @param dst destination file descriptor - * @param src_size size of source file as obtained by ::fstat(src) - * @return if the operation was successful + * \brief Performs an accelerated, in-kernel copy from src to dst + * \param src source file descriptor + * \param dst destination file descriptor + * \param src_size size of source file as obtained by ::fstat(src) + * \return if the operation was successful * * Relevant man pages: * - https://www.man7.org/linux/man-pages/man2/copy_file_range.2.html @@ -445,10 +445,10 @@ inline bool copy_file_dense_linux(const int src, const int dst, const off_t src_ } /** - * @brief performs a dense copy from source_path to destionation_path - * @param source_path path to source file - * @param destination_path path to destination file - * @return if the operation was successful + * \brief performs a dense copy from source_path to destionation_path + * \param source_path path to source file + * \param destination_path path to destination file + * \return if the operation was successful */ inline bool copy_file_dense_linux(const std::filesystem::path &source_path, const std::filesystem::path &destination_path) { diff --git a/verification/sparse_copy/verify_sparse_copy_syscalls.cpp b/verification/sparse_copy/verify_sparse_copy_syscalls.cpp index 0811d68a..633912c1 100644 --- a/verification/sparse_copy/verify_sparse_copy_syscalls.cpp +++ b/verification/sparse_copy/verify_sparse_copy_syscalls.cpp @@ -32,15 +32,15 @@ void fill_file(int fd); /** * Randomly punches 1-9 holes into fd. * - * @param hole_at_start forces a hole to be created at the start of the file - * @param hole_at_end forces a hole to be created at the end of the file + * \param hole_at_start forces a hole to be created at the start of the file + * \param hole_at_end forces a hole to be created at the end of the file */ void punch_holes(int fd, bool hole_at_start = false, bool hole_at_end = false); /** * Convenience wrapper around ::mmap - * @param fd file descriptor to mmap - * @return pair (pointer to mapped range, size of mapped range) + * \param fd file descriptor to mmap + * \return pair (pointer to mapped range, size of mapped range) */ std::pair mmap(int fd); From e7289b609756d93feafab6ec2a53209f5053006e Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:46:38 +0100 Subject: [PATCH 18/26] only build linux sparse copy verification on linux --- verification/sparse_copy/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt index e58dbbc0..10eebbcb 100644 --- a/verification/sparse_copy/CMakeLists.txt +++ b/verification/sparse_copy/CMakeLists.txt @@ -1,2 +1,5 @@ add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) -add_metall_executable(verify_sparse_copy_syscalls verify_sparse_copy_syscalls.cpp) \ No newline at end of file + +if (Linux) + add_metall_executable(verify_sparse_copy_syscalls verify_sparse_copy_syscalls.cpp) +endif () From bb2f85aa2b3ce3980dc32095e151340c16efbce0 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Tue, 30 Jan 2024 15:48:04 -0800 Subject: [PATCH 19/26] Bugfix: detect Linux in CMake --- CMakeLists.txt | 4 ++-- tutorial/nvmw21/CMakeLists.txt | 2 +- verification/sparse_copy/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f06a9f29..5c64eba4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,7 @@ function(add_common_compile_options name) target_compile_options(${name} PRIVATE $<$:-Og>) target_compile_options(${name} PRIVATE $<$:-g3>) target_compile_options(${name} PRIVATE $<$:-Wextra>) - if (Linux) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_options(${name} PRIVATE $<$:-pg>) endif () @@ -225,7 +225,7 @@ function(add_common_compile_options name) # Release with debug info target_compile_options(${name} PRIVATE $<$:-Ofast>) target_compile_options(${name} PRIVATE $<$:-g3>) - if (Linux) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_options(${name} PRIVATE $<$:-pg>) endif () endfunction() diff --git a/tutorial/nvmw21/CMakeLists.txt b/tutorial/nvmw21/CMakeLists.txt index 0efa9a25..901bf852 100644 --- a/tutorial/nvmw21/CMakeLists.txt +++ b/tutorial/nvmw21/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_FLAGS "-Wall -pthread") # Debug set(CMAKE_CXX_FLAGS_DEBUG "-O0") -if (Linux) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") endif () diff --git a/verification/sparse_copy/CMakeLists.txt b/verification/sparse_copy/CMakeLists.txt index 10eebbcb..7900ce67 100644 --- a/verification/sparse_copy/CMakeLists.txt +++ b/verification/sparse_copy/CMakeLists.txt @@ -1,5 +1,5 @@ add_metall_executable(verify_sparse_copy verify_sparse_copy.cpp) -if (Linux) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") add_metall_executable(verify_sparse_copy_syscalls verify_sparse_copy_syscalls.cpp) endif () From e0a73ea417c758ceae69b6947eb8429fe50a5847 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Wed, 31 Jan 2024 13:19:34 +0100 Subject: [PATCH 20/26] new c api --- example/c_api.c | 43 ++++++--- include/metall/c_api/metall.h | 176 +++++++++++++++++++++------------- src/metall_c.cpp | 117 +++++++++++++++------- 3 files changed, 225 insertions(+), 111 deletions(-) diff --git a/example/c_api.c b/example/c_api.c index 1db9c3a3..78345071 100644 --- a/example/c_api.c +++ b/example/c_api.c @@ -4,48 +4,65 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) #include +#include #include int main(void) { // Basic allocation { - metall_open(METALL_CREATE_ONLY, "/tmp/metall1"); + metall_manager* manager = metall_create("/tmp/metall1"); - uint64_t *x = metall_malloc(sizeof(uint64_t)); + uint64_t* x = metall_malloc(manager, sizeof(uint64_t)); x[0] = 1; - metall_free(x); - - metall_close(); + metall_free(manager, x); + metall_close(manager); + metall_remove("/tmp/metall1"); } // Allocate named object { - metall_open(METALL_CREATE_ONLY, "/tmp/metall2"); + metall_manager* manager = metall_create("/tmp/metall2"); - uint64_t *array = metall_named_malloc("array", sizeof(uint64_t) * 10); + uint64_t* array = metall_named_malloc(manager, "array", + sizeof(uint64_t) * 10); array[0] = 0; - metall_flush(); + metall_flush(manager); array[1] = 1; - metall_close(); + metall_snapshot(manager, "/tmp/metall2-snap"); + metall_close(manager); } // Retrieve named object { - metall_open(METALL_OPEN_ONLY, "/tmp/metall2"); + metall_manager* manager = metall_open("/tmp/metall2"); - uint64_t *array = metall_find("array"); + uint64_t* array = metall_find(manager, "array"); assert(array[0] == 0); assert(array[1] == 1); - metall_named_free("array"); + metall_named_free(manager, "array"); + metall_close(manager); + metall_remove("/tmp/metall2"); + } + + // Retrieve object snapshot + { + metall_manager* manager = metall_open("/tmp/metall2-snap"); + + uint64_t* array = metall_find(manager, "array"); + + assert(array[0] == 0); + assert(array[1] == 1); - metall_close(); + metall_named_free(manager, "array"); + metall_close(manager); + metall_remove("/tmp/metall2-snap"); } return 0; diff --git a/include/metall/c_api/metall.h b/include/metall/c_api/metall.h index 27a979c1..c9ea7387 100644 --- a/include/metall/c_api/metall.h +++ b/include/metall/c_api/metall.h @@ -1,4 +1,4 @@ -// Copyright 2019 Lawrence Livermore National Security, LLC and other Metall +// Copyright 2024 Lawrence Livermore National Security, LLC and other Metall // Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (Apache-2.0 OR MIT) @@ -6,75 +6,121 @@ #ifndef METALL_C_API_METALL_H #define METALL_C_API_METALL_H -#include +#include +#include #ifdef __cplusplus extern "C" { #endif -/// \brief Tag to create the segment always. -/// The existing segment with the same name is over written. -#define METALL_CREATE_ONLY 1 - -/// \brief Tag to open an already created segment. -#define METALL_OPEN_ONLY 2 - -/// \brief Tag to open an already created segment as read only. -#define METALL_OPEN_READ_ONLY 3 - -/// \brief Constructs a Metall manager object -/// \param mode Open mode -/// \param path A path to the backing data store -/// \return On success, returns 0. On error, returns -1. -extern int metall_open(int mode, const char *path); - -/// \brief Destructs Metall manager object -extern void metall_close(); - -/// \brief Flush data to persistent memory -extern void metall_flush(); - -/// \brief Allocates nbytes bytes. -/// \param nbytes The Number of bytes to allocate -/// \return Returns a pointer to the allocated memory -extern void *metall_malloc(uint64_t nbytes); - -/// \brief Frees the allocated memory -/// \param ptr A pointer to the allocated memory to be free -extern void metall_free(void *ptr); - -/// \brief Allocates nbytes bytes and save the address of the allocated memory -/// with name \param name A name of the allocated memory \param nbytes A size to -/// allocate \return Returns a pointer to the allocated memory -extern void *metall_named_malloc(const char *name, uint64_t nbytes); - -/// \brief Finds a saved memory -/// \param name A name of the allocated memory to find -/// \return Returns a pointer to the allocated memory if it exist. Otherwise, -/// returns NULL. -extern void *metall_find(char *name); - -/// \brief Frees memory with the name -/// \param name A name of the allocated memory to free -extern void metall_named_free(const char *name); - -/// \brief Snapshot the entire data. -/// \param destination_path The path to store a snapshot. -/// \return On success, returns 0. On error, returns -1. -extern int snapshot(const char *destination_path); - -/// \brief Copies backing files synchronously. -/// \param source_path Source data store path. -/// \param destination_path Destination data store path. -/// \return On success, returns 0. On error, returns -1. -extern int copy(const char *source_path, const char *destination_path); - -/// \brief Check if the backing data store is consistent, -/// i.e. it was closed properly. -/// \param path A path to the backing data store. -/// \return Returns a oon-zero integer if the data store is consistent; -/// otherwise, returns 0. -extern int consistent(const char *path); +/** + * \brief Opaque struct representing a metall manager + * \note this type is internally represented by `::metall::manager` therefore pointers + * to `::metall_manager` may be reinterpret-casted to pointers to `::metall::manager` + */ +typedef struct metall_manager metall_manager; + +/** + * \brief Attempts to open the metall datastore at path + * \param path path to datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - ENOTRECOVERABLE if the given metall datastore is inconsistent + */ +metall_manager* metall_open(const char* path); + +/** + * \brief Attempts to open the metall datastore at path in read only mode + * \param path path to datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - ENOTRECOVERABLE if the given metall datastore is inconsistent + */ +metall_manager* metall_open_read_only(const char* path); + +/** + * \brief Attempts to create a metall datastore at path + * \param path path at which to create a datastore + * \return true on success, false on failure. On failure, sets errno to one of the following values: + * - EEXIST if the given path already exists + * - ENOTRECOVERABLE if the datastore could not be created for some other reason + */ +metall_manager* metall_create(const char* path); + +/** + * \brief Creates a snapshot of the metall datastore of manager and places it at dst_path + * \param manager manager to perform snapshot + * \param dst_path path where to place the snapshot + * \return true if the snapshot was successfully created otherwise false. + */ +bool metall_snapshot(metall_manager* manager, const char* dst_path); + +/** + * \brief Flushes the given manager + * \param manager manager to flush + */ +void metall_flush(metall_manager* manager); + +/** + * \brief Closes a metall manager + */ +void metall_close(metall_manager* manager); + +/** + * \brief Removes the metall datastore at path + * \param path path to datastore to remove + * \return true on successful removal, false otherwise. On failure, sets errno to one of the following values: + * - EADDRINUSE if there is a metall manager open for the given path + * + * \warning Behaviour is undefined if there is still a metall manager for path open + */ +bool metall_remove(const char* path); + +/** + * \brief Allocates size bytes + * \param manager manager to allocate with + * \param size number of bytes to allocate + * \return pointer to allocated memory if successful otherwise returns NULL and sets errno to one of the following values + * - EINVAL + */ +void* metall_malloc(metall_manager* manager, size_t size); + +/** + * \brief Frees memory previously allocated by metall_malloc + * \param manager manager from which to free + * \param ptr memory to free + */ +void metall_free(metall_manager* manager, void* ptr); + +/** + * \brief Allocates size bytes and associates the allocated memory with a name + * \param manager manager to allocate with + * \param name A name of the allocated memory + * \param size number of bytes to allocate + * \return pointer to the allocated memory if sucessful otherwise returns NULL and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOMEM if the memory could not be allocated + */ +void* metall_named_malloc(metall_manager* manager, const char* name, + size_t size); + +/** + * \brief Finds memory that was previously allocated using metall_named_alloc + * \param manager manager to find the object in + * \param name name of the allocated memory to find + * \return pointer to the allocated memory if found. Otherwise, returns NULL and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOTENT if the object could not be found + */ +void* metall_find(metall_manager* manager, const char* name); + +/** + * \brief Frees memory previously allocated by metall_named_malloc + * \param manager manager from which to free + * \param name name of the allocated memory to free + * \return true if sucessfully freed, otherwise returns false and sets errno to one of the following values + * - EINVAL if the given path does not have a metall datastore open + * - ENOENT if the referred to object does not exist + */ +bool metall_named_free(metall_manager* manager, const char* name); #ifdef __cplusplus } diff --git a/src/metall_c.cpp b/src/metall_c.cpp index 5c920481..7efc98ca 100644 --- a/src/metall_c.cpp +++ b/src/metall_c.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 Lawrence Livermore National Security, LLC and other Metall +// Copyright 2024 Lawrence Livermore National Security, LLC and other Metall // Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (Apache-2.0 OR MIT) @@ -6,55 +6,106 @@ #include #include -metall::manager *g_manager = nullptr; +template +metall_manager* open_impl(const char* path) { + if (!metall::manager::consistent(path)) { + // prevents opening the same datastore twice + // (because opening removes the properly_closed_mark and this checks for it) + errno = ENOTRECOVERABLE; + return nullptr; + } -int metall_open(const int mode, const char *const path) { - if (mode == METALL_CREATE_ONLY) { - g_manager = new metall::manager(metall::create_only, path); - } else if (mode == METALL_OPEN_ONLY) { - g_manager = new metall::manager(metall::open_only, path); - } else if (mode == METALL_OPEN_READ_ONLY) { - g_manager = new metall::manager(metall::open_read_only, path); - } else { - g_manager = nullptr; + auto* manager = new metall::manager{open_mode{}, path}; + if (!manager->check_sanity()) { + delete manager; + errno = ENOTRECOVERABLE; + return nullptr; } - if (g_manager) { - return 0; - } else { - return -1; // error + return reinterpret_cast(manager); +} + +metall_manager* metall_open(const char* path) { + return open_impl(path); +} + +metall_manager* metall_open_read_only(const char* path) { + return open_impl(path); +} + +metall_manager* metall_create(const char* path) { + if (std::filesystem::exists(path)) { + // prevent accidental overwrite + errno = EEXIST; + return nullptr; + } + + auto* manager = new metall::manager{metall::create_only, path}; + if (!manager->check_sanity()) { + delete manager; + errno = ENOTRECOVERABLE; + return nullptr; } + + return reinterpret_cast(manager); } -void metall_close() { delete g_manager; } +bool metall_snapshot(metall_manager* manager, const char* dst_path) { + return reinterpret_cast(manager)->snapshot(dst_path); +} -void metall_flush() { g_manager->flush(); } +void metall_flush(metall_manager* manager) { + reinterpret_cast(manager)->flush(); +} -void *metall_malloc(const uint64_t nbytes) { - return g_manager->allocate(nbytes); +void metall_close(metall_manager* manager) { + delete reinterpret_cast(manager); } -void metall_free(void *const ptr) { g_manager->deallocate(ptr); } +bool metall_remove(const char* path) { + return metall::manager::remove(path); +} -void *metall_named_malloc(const char *name, const uint64_t nbytes) { - return g_manager->construct(name)[nbytes](); +void* metall_malloc(metall_manager* manager, size_t size) { + auto* ptr = reinterpret_cast(manager)->allocate(size); + if (ptr == nullptr) { + errno = ENOMEM; + } + + return ptr; } -void *metall_find(char *name) { return g_manager->find(name).first; } +void metall_free(metall_manager* manager, void* ptr) { + reinterpret_cast(manager)->deallocate(ptr); +} -void metall_named_free(const char *name) { g_manager->destroy(name); } +void* metall_named_malloc(metall_manager* manager, const char* name, + size_t size) { + auto* ptr = reinterpret_cast(manager)->construct(name)[size](); + if (ptr == nullptr) { + errno = ENOMEM; + } -int snapshot(const char *destination_path) { - if (g_manager->snapshot(destination_path)) return 0; - return -1; // Error + return ptr; } -int copy(const char *source_path, const char *destination_path) { - if (metall::manager::copy(source_path, destination_path)) return 0; - return -1; // Error +void* metall_find(metall_manager* manager, const char* name) { + auto* ptr = reinterpret_cast(manager)->find(name).first; + if (ptr == nullptr) { + errno = ENOENT; + } + + return ptr; } -int consistent(const char *path) { - if (metall::manager::consistent(path)) return 1; - return 0; +bool metall_named_free(metall_manager* manager, const char* name) { + auto const res = reinterpret_cast(manager)->destroy(name); + if (!res) { + errno = ENOENT; + } + + return res; } \ No newline at end of file From 881239acb8270c1da16bcd2528505ad356800ec0 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Wed, 14 Feb 2024 13:11:32 -0800 Subject: [PATCH 21/26] Remove propagate_on_* types and is_always_equal --- .../utility/fallback_allocator_adaptor.hpp | 13 -------- .../fallback_allocator_adaptor_test.cpp | 32 ------------------- 2 files changed, 45 deletions(-) diff --git a/include/metall/utility/fallback_allocator_adaptor.hpp b/include/metall/utility/fallback_allocator_adaptor.hpp index 8e93e70c..8e96d040 100644 --- a/include/metall/utility/fallback_allocator_adaptor.hpp +++ b/include/metall/utility/fallback_allocator_adaptor.hpp @@ -55,10 +55,6 @@ class fallback_allocator_adaptor { typename primary_allocator_type::const_void_pointer; using difference_type = typename primary_allocator_type::difference_type; using size_type = typename primary_allocator_type::size_type; - using propagate_on_container_copy_assignment = std::true_type; - using propagate_on_container_move_assignment = std::true_type; - using propagate_on_container_swap = std::true_type; - using is_always_equal = std::false_type; /// \brief Makes another allocator type for type T2 template @@ -205,15 +201,6 @@ class fallback_allocator_adaptor { } } - /// \brief Obtains the copy-constructed version of the allocator a. - /// \param a Allocator used by a standard container passed as an argument to a - /// container copy constructor. \return The allocator to use by the - /// copy-constructed standard containers. - fallback_allocator_adaptor select_on_container_copy_construction( - const fallback_allocator_adaptor &a) { - return fallback_allocator_adaptor(a); - } - // ---------- This class's unique public functions ---------- // primary_allocator_type &primary_allocator() { return m_primary_allocator; } diff --git a/test/container/fallback_allocator_adaptor_test.cpp b/test/container/fallback_allocator_adaptor_test.cpp index 4a1443a9..1a685b4f 100644 --- a/test/container/fallback_allocator_adaptor_test.cpp +++ b/test/container/fallback_allocator_adaptor_test.cpp @@ -66,38 +66,6 @@ TEST(FallbackAllocatorAdaptorTest, Types) { GTEST_ASSERT_EQ(typeid(std::allocator_traits>::size_type), typeid(fb_alloc_type::size_type)); - GTEST_ASSERT_EQ( - typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_copy_assignment), - typeid(fb_alloc_type::propagate_on_container_copy_assignment)); - GTEST_ASSERT_EQ( - typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_copy_assignment), - typeid(std::true_type)); - - GTEST_ASSERT_EQ( - typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_move_assignment), - typeid(fb_alloc_type::propagate_on_container_move_assignment)); - GTEST_ASSERT_EQ( - typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_move_assignment), - typeid(std::true_type)); - - GTEST_ASSERT_EQ(typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_swap), - typeid(fb_alloc_type::propagate_on_container_swap)); - GTEST_ASSERT_EQ(typeid(std::allocator_traits< - fb_alloc_type>::propagate_on_container_swap), - typeid(std::true_type)); - - GTEST_ASSERT_EQ( - typeid(std::allocator_traits>::is_always_equal), - typeid(fb_alloc_type::is_always_equal)); - GTEST_ASSERT_EQ( - typeid(std::allocator_traits>::is_always_equal), - typeid(std::false_type)); - using otherT = int; using other_alloc_t = fb_alloc_type; GTEST_ASSERT_EQ( From 6b204d0b6b898258621f2d48433828844f9a3e08 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Wed, 14 Feb 2024 17:02:56 -0800 Subject: [PATCH 22/26] Reorganize fallback allocator. --- example/CMakeLists.txt | 2 +- example/README.md | 4 +- example/fallback_allocator.cpp | 33 +++ example/fallback_allocator_adaptor.cpp | 41 --- include/metall/basic_manager.hpp | 33 +++ .../metall/container/fallback_allocator.hpp | 272 ++++++++++++++++++ .../utility/fallback_allocator_adaptor.hpp | 262 +---------------- test/container/CMakeLists.txt | 2 +- ...r_test.cpp => fallback_allocator_test.cpp} | 4 +- 9 files changed, 349 insertions(+), 304 deletions(-) create mode 100644 example/fallback_allocator.cpp delete mode 100644 example/fallback_allocator_adaptor.cpp create mode 100644 include/metall/container/fallback_allocator.hpp rename test/container/{fallback_allocator_adaptor_test.cpp => fallback_allocator_test.cpp} (98%) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 419934d0..92144777 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -14,7 +14,7 @@ add_metall_executable(snapshot snapshot.cpp) add_metall_executable(csr_graph csr_graph.cpp) -add_metall_executable(fallback_allocator_adaptor fallback_allocator_adaptor.cpp) +add_metall_executable(fallback_allocator fallback_allocator.cpp) add_metall_executable(datastore_description datastore_description.cpp) diff --git a/example/README.md b/example/README.md index f5da344b..3e2a939f 100644 --- a/example/README.md +++ b/example/README.md @@ -96,9 +96,9 @@ Therefore, the data structures in the examples below are more like how to use ST ### Fallback Allocator -* [fallback_allocator_adaptor.cpp](fallback_allocator_adaptor.cpp) +* [fallback_allocator.cpp](fallback_allocator.cpp) - * How to use [fallback_allocator_adaptor](../include/metall/utility/fallback_allocator_adaptor.hpp) + * How to use [fallback_allocator](../include/metall/container/fallback_allocator.hpp) ### MPI (experimental implementation) diff --git a/example/fallback_allocator.cpp b/example/fallback_allocator.cpp new file mode 100644 index 00000000..983e181a --- /dev/null +++ b/example/fallback_allocator.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#include + +#include +#include + +using vector_t = + metall::container::vector>; + +int main() { + // Allocation using Metall + { + metall::manager manager(metall::create_only, "/tmp/dir"); + auto pvec = manager.construct("vec")(manager.get_allocator()); + pvec->push_back(1); + std::cout << (*pvec)[0] << std::endl; + } + + // Allocation not using Metall, i.e., uses a heap allocator (malloc()). + // This code would cause a build error if fallback_allocator were not used. + { + vector_t vec; // As no metall allocator argument is passed, the fallback + // allocator uses malloc() internally. + vec.push_back(2); + std::cout << vec[0] << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/example/fallback_allocator_adaptor.cpp b/example/fallback_allocator_adaptor.cpp deleted file mode 100644 index e4d291fd..00000000 --- a/example/fallback_allocator_adaptor.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 Lawrence Livermore National Security, LLC and other Metall -// Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -#include - -#include - -#include -#include - -// The line below is the only change required to use fallback_allocator_adaptor. -// Wraps up 'metall::manager::allocator_type<..>' with -// fallback_allocator_adaptor. -using allocator_t = metall::utility::fallback_allocator_adaptor< - metall::manager::allocator_type>; - -using vector_t = boost::container::vector; - -int main() { - // Allocation with Metall - // The code below works with both 'fallback_allocator_adaptor<..>' and - // 'metall::manager::allocator_type<...>'. - { - metall::manager manager(metall::create_only, "/tmp/dir"); - auto pvec = manager.construct("vec")(manager.get_allocator()); - pvec->push_back(1); - std::cout << (*pvec)[0] << std::endl; - } - - // Allocation w/o Metall, i.e., uses a heap allocator such as malloc(). - // This code causes a build error if fallback_allocator_adaptor is not used. - { - vector_t vec; - vec.push_back(2); - std::cout << vec[0] << std::endl; - } - - return 0; -} \ No newline at end of file diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index fd45270f..f2728e56 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,38 @@ class basic_manager { container::scoped_allocator_adaptor, allocator_type...>; + /// \brief A STL compatible allocator which fallbacks to a heap allocator + /// (e.g., malloc()) if no argument is provided to construct a + /// allocator_type instance. + /// + /// \tparam T The type of the object to allocate + /// + /// \details + /// This allocator enables the following code. + /// \code + /// { + /// using alloc = fallback_allocator; + /// // Allocate a vector object in a heap. + /// vector vec; + /// // Allocate a vector object in a Metall space. + /// vector vec2(manager.get_allocator()); + /// } + /// \endcode + /// \attention + /// One of the primary purposes of this allocator is to provide a way to + /// temporarily allocate data structures that use Metall’s STL-allocator in a + /// heap in addition to in Metall memory space. It is advised to use this + /// allocator with caution as this two memory spaces are used transparently by + /// this allocator. + template + using fallback_allocator = + container::fallback_allocator_adaptor>; + + /// \brief Fallback allocator type wrapped by scoped_allocator_adaptor. + template + using scoped_fallback_allocator_type = + container::scoped_allocator_adaptor>; + /// \brief Construct proxy template using construct_proxy = diff --git a/include/metall/container/fallback_allocator.hpp b/include/metall/container/fallback_allocator.hpp new file mode 100644 index 00000000..cb435299 --- /dev/null +++ b/include/metall/container/fallback_allocator.hpp @@ -0,0 +1,272 @@ +// Copyright 2024 Lawrence Livermore National Security, LLC and other Metall +// Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +#ifndef METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP +#define METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP + +#include +#include +#include + +namespace metall::container { + +/// \brief A STL compatible allocator which fallbacks to a heap allocator (e.g., +/// malloc()) if its constructor receives no argument to construct the stateful +/// allocator instance. +/// \tparam stateful_allocator The stateful allocator type. It must not be +/// default constructible. +template +class fallback_allocator_adaptor { + // Check if the stateful_allocator takes arugments in its constructor + static_assert(!std::is_constructible::value, + "The stateful allocator must not be default constructible"); + + private: + template + using other_stateful_allocatorator_type = typename std::allocator_traits< + stateful_allocator>::template rebind_alloc; + + public: + // -------------------- // + // Public types and static values + // -------------------- // + using stateful_allocatorator_type = typename std::remove_const< + typename std::remove_reference::type>::type; + + using value_type = typename stateful_allocatorator_type::value_type; + using pointer = typename stateful_allocatorator_type::pointer; + using const_pointer = typename stateful_allocatorator_type::const_pointer; + using void_pointer = typename stateful_allocatorator_type::void_pointer; + using const_void_pointer = + typename stateful_allocatorator_type::const_void_pointer; + using difference_type = typename stateful_allocatorator_type::difference_type; + using size_type = typename stateful_allocatorator_type::size_type; + + /// \brief Makes another allocator type for type T2 + template + struct rebind { + using other = + fallback_allocator_adaptor>; + }; + + public: + // -------------------- // + // Constructor & assign operator + // -------------------- // + + /// \brief Default constructor which falls back on the regular allocator + /// (i.e., malloc()). + fallback_allocator_adaptor() noexcept : m_stateful_allocatorator(nullptr) {} + + /// \brief Construct a new instance using an instance of + /// fallback_allocator_adaptor with any stateful_allocatorator type. + template ::value, + int> = 0> + fallback_allocator_adaptor( + fallback_allocator_adaptor + allocator_instance) noexcept + : m_stateful_allocatorator(allocator_instance.stateful_allocatorator()) {} + + /// \brief Construct a new instance using an instance of any + /// stateful_allocatorator. + template ::value, + int> = 0> + fallback_allocator_adaptor( + stateful_allocatorator_type2 allocator_instance) noexcept + : m_stateful_allocatorator(allocator_instance) {} + + /// \brief Copy constructor + fallback_allocator_adaptor(const fallback_allocator_adaptor &other) noexcept = + default; + + /// \brief Move constructor + fallback_allocator_adaptor(fallback_allocator_adaptor &&other) noexcept = + default; + + /// \brief Copy assign operator + fallback_allocator_adaptor &operator=( + const fallback_allocator_adaptor &) noexcept = default; + + /// \brief Copy assign operator, using an instance of + /// fallback_allocator_adaptor with any stateful_allocatorator type + template ::value, + int> = 0> + fallback_allocator_adaptor &operator=( + const fallback_allocator_adaptor + &other) noexcept { + m_stateful_allocatorator = other.stateful_allocatorator(); + return *this; + } + + /// \brief Copy assign operator for any stateful_allocatorator + template ::value, + int> = 0> + fallback_allocator_adaptor &operator=( + const stateful_allocatorator_type2 &allocator_instance) noexcept { + m_stateful_allocatorator = allocator_instance; + return *this; + } + + /// \brief Move assign operator + fallback_allocator_adaptor &operator=( + fallback_allocator_adaptor &&other) noexcept = default; + + /// \brief Move assign operator, using an instance of + /// fallback_allocator_adaptor with any stateful_allocatorator type + template ::value, + int> = 0> + fallback_allocator_adaptor &operator=( + fallback_allocator_adaptor + &&other) noexcept { + m_stateful_allocatorator = std::move(other.stateful_allocatorator()); + return *this; + } + + /// \brief Move assign operator for any stateful_allocatorator + template ::value, + int> = 0> + fallback_allocator_adaptor &operator=( + stateful_allocatorator_type2 &&allocator_instance) noexcept { + m_stateful_allocatorator = std::move(allocator_instance); + return *this; + } + + /// \brief Allocates n * sizeof(T) bytes of storage + /// \param n The size to allocation + /// \return Returns a pointer + pointer allocate(const size_type n) const { + if (priv_stateful_allocatorator_available()) { + return m_stateful_allocatorator.allocate(n); + } + return priv_fallback_allocate(n); + } + + /// \brief Deallocates the storage reference by the pointer ptr + /// \param ptr A pointer to the storage + /// \param size The size of the storage + void deallocate(pointer ptr, const size_type size) const { + if (priv_stateful_allocatorator_available()) { + m_stateful_allocatorator.deallocate(ptr, size); + } else { + priv_fallback_deallocate(ptr); + } + } + + /// \brief The size of the theoretical maximum allocation size + /// \return The size of the theoretical maximum allocation size + size_type max_size() const noexcept { + return m_stateful_allocatorator.max_size(); + } + + /// \brief Constructs an object of T + /// \tparam Args The types of the constructor arguments + /// \param ptr A pointer to allocated storage + /// \param args The constructor arguments to use + template + void construct(const pointer &ptr, Args &&...args) const { + if (priv_stateful_allocatorator_available()) { + m_stateful_allocatorator.construct(ptr, std::forward(args)...); + } else { + priv_fallback_construct(ptr, std::forward(args)...); + } + } + + /// \brief Deconstruct an object of T + /// \param ptr A pointer to the object + void destroy(const pointer &ptr) const { + if (priv_stateful_allocatorator_available()) { + m_stateful_allocatorator.destroy(ptr); + } else { + priv_fallback_destroy(ptr); + } + } + + // ---------- This class's unique public functions ---------- // + stateful_allocatorator_type &stateful_allocatorator() { + return m_stateful_allocatorator; + } + + const stateful_allocatorator_type &stateful_allocatorator() const { + return m_stateful_allocatorator; + } + + private: + // -------------------- // + // Private methods + // -------------------- // + auto priv_stateful_allocatorator_available() const { + return !!(m_stateful_allocatorator.get_pointer_to_manager_kernel()); + } + + pointer priv_fallback_allocate(const size_type n) const { + if (max_size() < n) { + throw std::bad_array_new_length(); + } + + void *const addr = std::malloc(n * sizeof(value_type)); + if (!addr) { + throw std::bad_alloc(); + } + + return pointer(static_cast(addr)); + } + + void priv_fallback_deallocate(pointer ptr) const { + std::free(to_raw_pointer(ptr)); + } + + void priv_fallback_destroy(pointer ptr) const { (*ptr).~value_type(); } + + template + void priv_fallback_construct(const pointer &ptr, arg_types &&...args) const { + ::new ((void *)to_raw_pointer(ptr)) + value_type(std::forward(args)...); + } + + // -------------------- // + // Private fields + // -------------------- // + stateful_allocatorator_type m_stateful_allocatorator; +}; + +template +inline bool operator==( + const fallback_allocator_adaptor &rhd, + const fallback_allocator_adaptor &lhd) { + // Return true if they point to the same manager kernel + return rhd.stateful_allocatorator() == lhd.stateful_allocatorator(); +} + +template +inline bool operator!=( + const fallback_allocator_adaptor &rhd, + const fallback_allocator_adaptor &lhd) { + return !(rhd == lhd); +} + +} // namespace metall::container + +/// \example fallback_allocator.cpp +/// This is an example of how to use fallback_allocator. + +#endif // METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP diff --git a/include/metall/utility/fallback_allocator_adaptor.hpp b/include/metall/utility/fallback_allocator_adaptor.hpp index 8e96d040..923abfea 100644 --- a/include/metall/utility/fallback_allocator_adaptor.hpp +++ b/include/metall/utility/fallback_allocator_adaptor.hpp @@ -6,265 +6,15 @@ #ifndef METALL_UTILITY_FALLBACK_ALLOCATOR_ADAPTOR_HPP #define METALL_UTILITY_FALLBACK_ALLOCATOR_ADAPTOR_HPP -#include -#include -#include +#include "metall/container/fallback_allocator.hpp" -#include +#warning \ + "This file is deprecated. Use container/fallback_allocator.hpp instead." namespace metall::utility { - -/// \brief A STL compatible allocator which fallbacks to a heap allocator (e.g., -/// malloc) if its constructor receives no argument. \tparam primary_alloc A -/// primary allocator type, i.e., metall::stl_allocator. -/// -/// \details -/// Using this allocator, the following code will work: -/// \code -/// { -/// using alloc = -/// fallback_allocator_adaptor>; -/// vector temp_vec; // Allocate an vector object temporarily in -/// 'DRAM'. -/// } -/// \endcode -/// \attention -/// The purpose of this allocator is providing a way to quickly integrate Metall -/// into an application which wants to allocate 'metallized' classes temporarily -/// in 'DRAM' occasionally. This allocator could cause difficult bugs to debug. -/// Use this allocator with great care. template -class fallback_allocator_adaptor { - private: - template - using other_primary_allocator_type = - typename std::allocator_traits::template rebind_alloc; - - public: - // -------------------- // - // Public types and static values - // -------------------- // - using primary_allocator_type = typename std::remove_const< - typename std::remove_reference::type>::type; - - using value_type = typename primary_allocator_type::value_type; - using pointer = typename primary_allocator_type::pointer; - using const_pointer = typename primary_allocator_type::const_pointer; - using void_pointer = typename primary_allocator_type::void_pointer; - using const_void_pointer = - typename primary_allocator_type::const_void_pointer; - using difference_type = typename primary_allocator_type::difference_type; - using size_type = typename primary_allocator_type::size_type; - - /// \brief Makes another allocator type for type T2 - template - struct rebind { - using other = fallback_allocator_adaptor>; - }; - - public: - // -------------------- // - // Constructor & assign operator - // -------------------- // - - /// \brief Default constructor which falls back on a heap allocator. - fallback_allocator_adaptor() noexcept : m_primary_allocator(nullptr) {} - - /// \brief Construct a new instance using an instance of - /// fallback_allocator_adaptor with any primary_allocator type. - template ::value, - int> = 0> - fallback_allocator_adaptor(fallback_allocator_adaptor - allocator_instance) noexcept - : m_primary_allocator(allocator_instance.primary_allocator()) {} - - /// \brief Construct a new instance using an instance of any - /// primary_allocator. - template ::value, - int> = 0> - fallback_allocator_adaptor( - primary_allocator_type2 allocator_instance) noexcept - : m_primary_allocator(allocator_instance) {} - - /// \brief Copy constructor - fallback_allocator_adaptor(const fallback_allocator_adaptor &other) noexcept = - default; - - /// \brief Move constructor - fallback_allocator_adaptor(fallback_allocator_adaptor &&other) noexcept = - default; - - /// \brief Copy assign operator - fallback_allocator_adaptor &operator=( - const fallback_allocator_adaptor &) noexcept = default; - - /// \brief Copy assign operator, using an instance of - /// fallback_allocator_adaptor with any primary_allocator type - template ::value, - int> = 0> - fallback_allocator_adaptor &operator=( - const fallback_allocator_adaptor - &other) noexcept { - m_primary_allocator = other.primary_allocator(); - return *this; - } - - /// \brief Copy assign operator for any primary_allocator - template ::value, - int> = 0> - fallback_allocator_adaptor &operator=( - const primary_allocator_type2 &allocator_instance) noexcept { - m_primary_allocator = allocator_instance; - return *this; - } - - /// \brief Move assign operator - fallback_allocator_adaptor &operator=( - fallback_allocator_adaptor &&other) noexcept = default; - - /// \brief Move assign operator, using an instance of - /// fallback_allocator_adaptor with any primary_allocator type - template ::value, - int> = 0> - fallback_allocator_adaptor &operator=( - fallback_allocator_adaptor &&other) noexcept { - m_primary_allocator = std::move(other.primary_allocator()); - return *this; - } - - /// \brief Move assign operator for any primary_allocator - template ::value, - int> = 0> - fallback_allocator_adaptor &operator=( - primary_allocator_type2 &&allocator_instance) noexcept { - m_primary_allocator = std::move(allocator_instance); - return *this; - } - - /// \brief Allocates n * sizeof(T) bytes of storage - /// \param n The size to allocation - /// \return Returns a pointer - pointer allocate(const size_type n) const { - if (priv_primary_allocator_available()) { - return m_primary_allocator.allocate(n); - } - return priv_fallback_allocate(n); - } - - /// \brief Deallocates the storage reference by the pointer ptr - /// \param ptr A pointer to the storage - /// \param size The size of the storage - void deallocate(pointer ptr, const size_type size) const { - if (priv_primary_allocator_available()) { - m_primary_allocator.deallocate(ptr, size); - } else { - priv_fallback_deallocate(ptr); - } - } - - /// \brief The size of the theoretical maximum allocation size - /// \return The size of the theoretical maximum allocation size - size_type max_size() const noexcept { return m_primary_allocator.max_size(); } - - /// \brief Constructs an object of T - /// \tparam Args The types of the constructor arguments - /// \param ptr A pointer to allocated storage - /// \param args The constructor arguments to use - template - void construct(const pointer &ptr, Args &&...args) const { - if (priv_primary_allocator_available()) { - m_primary_allocator.construct(ptr, std::forward(args)...); - } else { - priv_fallback_construct(ptr, std::forward(args)...); - } - } - - /// \brief Deconstruct an object of T - /// \param ptr A pointer to the object - void destroy(const pointer &ptr) const { - if (priv_primary_allocator_available()) { - m_primary_allocator.destroy(ptr); - } else { - priv_fallback_destroy(ptr); - } - } - - // ---------- This class's unique public functions ---------- // - primary_allocator_type &primary_allocator() { return m_primary_allocator; } - - const primary_allocator_type &primary_allocator() const { - return m_primary_allocator; - } - - private: - // -------------------- // - // Private methods - // -------------------- // - auto priv_primary_allocator_available() const { - return !!(m_primary_allocator.get_pointer_to_manager_kernel()); - } - - pointer priv_fallback_allocate(const size_type n) const { - if (max_size() < n) { - throw std::bad_array_new_length(); - } - - void *const addr = std::malloc(n * sizeof(value_type)); - if (!addr) { - throw std::bad_alloc(); - } - - return pointer(static_cast(addr)); - } - - void priv_fallback_deallocate(pointer ptr) const { - std::free(to_raw_pointer(ptr)); - } - - void priv_fallback_destroy(pointer ptr) const { (*ptr).~value_type(); } - - template - void priv_fallback_construct(const pointer &ptr, arg_types &&...args) const { - ::new ((void *)to_raw_pointer(ptr)) - value_type(std::forward(args)...); - } - - // -------------------- // - // Private fields - // -------------------- // - primary_allocator_type m_primary_allocator; -}; - -template -inline bool operator==( - const fallback_allocator_adaptor &rhd, - const fallback_allocator_adaptor &lhd) { - // Return true if they point to the same manager kernel - return rhd.primary_allocator() == lhd.primary_allocator(); +using fallback_allocator_adaptor = + metall::container::fallback_allocator_adaptor; } -template -inline bool operator!=( - const fallback_allocator_adaptor &rhd, - const fallback_allocator_adaptor &lhd) { - return !(rhd == lhd); -} - -} // namespace metall::utility - -/// \example fallback_allocator_adaptor.cpp -/// This is an example of how to use fallback_allocator_adaptor. - -#endif // METALL_UTILITY_FALLBACK_ALLOCATOR_ADAPTOR_HPP +#endif \ No newline at end of file diff --git a/test/container/CMakeLists.txt b/test/container/CMakeLists.txt index 4715b596..afb1c4e4 100644 --- a/test/container/CMakeLists.txt +++ b/test/container/CMakeLists.txt @@ -2,7 +2,7 @@ add_metall_test_executable(concurrent_map_test concurrent_map_test.cpp) add_metall_test_executable(stl_allocator_test stl_allocator_test.cpp) -add_metall_test_executable(fallback_allocator_adaptor_test fallback_allocator_adaptor_test.cpp) +add_metall_test_executable(fallback_allocator_test fallback_allocator_test.cpp) add_metall_test_executable(string_key_store_test string_key_store_test.cpp) diff --git a/test/container/fallback_allocator_adaptor_test.cpp b/test/container/fallback_allocator_test.cpp similarity index 98% rename from test/container/fallback_allocator_adaptor_test.cpp rename to test/container/fallback_allocator_test.cpp index 1a685b4f..1fc8e2f2 100644 --- a/test/container/fallback_allocator_adaptor_test.cpp +++ b/test/container/fallback_allocator_test.cpp @@ -10,12 +10,10 @@ #include #include #include -#include #include "../test_utility.hpp" template -using fb_alloc_type = metall::utility::fallback_allocator_adaptor< - metall::manager::allocator_type>; +using fb_alloc_type = metall::manager::fallback_allocator; const std::string &dir_path() { const static std::string path(test_utility::make_test_path()); From b21169e9b91598227f0faf2e5acf5288a064b713 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+Clueliss@users.noreply.github.com> Date: Tue, 12 Mar 2024 07:51:27 +0100 Subject: [PATCH 23/26] implement metall::manager::read_only --- include/metall/basic_manager.hpp | 17 +++++++++++++++++ include/metall/kernel/manager_kernel.hpp | 4 ++++ include/metall/kernel/manager_kernel_impl.ipp | 5 +++++ test/kernel/manager_test.cpp | 9 +++++++++ 4 files changed, 35 insertions(+) diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index fd45270f..8479a4df 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -1345,6 +1345,23 @@ class basic_manager { return 0; } + /// \brief Returns if this manager was opened as read-only + /// \copydoc doc_thread_safe + /// + /// \return whether or not this manager was opened as read-only + bool read_only() const noexcept { + if (!check_sanity()) { + return true; + } + try { + return m_kernel->read_only(); + } catch (...) { + logger::out(logger::level::error, __FILE__, __LINE__, + "An exception has been thrown"); + } + return true; + } + // bool belongs_to_segment (const void *ptr) const /// \brief Checks the sanity. diff --git a/include/metall/kernel/manager_kernel.hpp b/include/metall/kernel/manager_kernel.hpp index 9ec8d0d3..5425f6d5 100644 --- a/include/metall/kernel/manager_kernel.hpp +++ b/include/metall/kernel/manager_kernel.hpp @@ -347,6 +347,10 @@ class manager_kernel { /// \return Returns the size of the application data segment. size_type get_segment_size() const; + /// \brief Returns if this kernel was opened as read-only + /// \return whether this kernel is read-only + bool read_only() const; + /// \brief Takes a snapshot. The snapshot has a different UUID. /// \param destination_base_path Destination path /// \param clone Use clone (reflink) to copy data. diff --git a/include/metall/kernel/manager_kernel_impl.ipp b/include/metall/kernel/manager_kernel_impl.ipp index 0ed49374..f2149ac1 100644 --- a/include/metall/kernel/manager_kernel_impl.ipp +++ b/include/metall/kernel/manager_kernel_impl.ipp @@ -443,6 +443,11 @@ manager_kernel::get_segment_size() const { return m_segment_storage.size(); } +template +bool manager_kernel::read_only() const { + return m_segment_storage.read_only(); +} + template bool manager_kernel::snapshot( const path_type &destination_base_path, const bool clone, diff --git a/test/kernel/manager_test.cpp b/test/kernel/manager_test.cpp index 95bf322d..579b0207 100644 --- a/test/kernel/manager_test.cpp +++ b/test/kernel/manager_test.cpp @@ -36,11 +36,13 @@ TEST(ManagerTest, CreateAndOpenModes) { manager_type::remove(dir_path()); { manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); + ASSERT_FALSE(manager.read_only()); ASSERT_NE(manager.construct("int")(10), nullptr); ASSERT_TRUE(manager.destroy("int")); } { manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); + ASSERT_FALSE(manager.read_only()); auto ret = manager.find("int"); ASSERT_EQ(ret.first, nullptr); ASSERT_FALSE(manager.destroy("int")); @@ -51,10 +53,12 @@ TEST(ManagerTest, CreateAndOpenModes) { manager_type::remove(dir_path()); { manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); + ASSERT_FALSE(manager.read_only()); ASSERT_NE(manager.construct("int")(10), nullptr); } { manager_type manager(metall::open_only, dir_path()); + ASSERT_FALSE(manager.read_only()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -66,16 +70,19 @@ TEST(ManagerTest, CreateAndOpenModes) { manager_type::remove(dir_path()); { manager_type manager(metall::create_only, dir_path(), 1UL << 30UL); + ASSERT_FALSE(manager.read_only()); ASSERT_NE(manager.construct("int")(10), nullptr); } { manager_type manager(metall::open_read_only, dir_path()); + ASSERT_TRUE(manager.read_only()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); } { manager_type manager(metall::open_only, dir_path()); + ASSERT_FALSE(manager.read_only()); auto ret = manager.find("int"); ASSERT_NE(ret.first, nullptr); ASSERT_EQ(*(static_cast(ret.first)), 10); @@ -1331,12 +1338,14 @@ TEST(ManagerTest, CheckSanity) { { auto *manager = new manager_type(metall::create_only, dir_path()); ASSERT_TRUE(manager->check_sanity()); + ASSERT_FALSE(manager->read_only()); } { auto *bad_manager = new manager_type(metall::open_only, dir_path().string() + "-invalid"); ASSERT_FALSE(bad_manager->check_sanity()); + ASSERT_TRUE(bad_manager->read_only()); } } } // namespace From cdf16083109fe4ea5fff41a0d568318edced4776 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Sun, 14 Apr 2024 14:39:47 -0700 Subject: [PATCH 24/26] Minor comment fix --- include/metall/basic_manager.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index f2728e56..2bbd05ed 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -91,7 +91,7 @@ class basic_manager { /// One of the primary purposes of this allocator is to provide a way to /// temporarily allocate data structures that use Metall’s STL-allocator in a /// heap in addition to in Metall memory space. It is advised to use this - /// allocator with caution as this two memory spaces are used transparently by + /// allocator with caution as two memory spaces are used transparently by /// this allocator. template using fallback_allocator = @@ -99,7 +99,7 @@ class basic_manager { /// \brief Fallback allocator type wrapped by scoped_allocator_adaptor. template - using scoped_fallback_allocator_type = + using scoped_fallback_allocator_type = container::scoped_allocator_adaptor>; /// \brief Construct proxy From 2aca0494d42329c592bbb7c141f3c10fc8eb47c5 Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Sun, 14 Apr 2024 14:44:05 -0700 Subject: [PATCH 25/26] Bugfix in MPI adaptor build --- cmake/setup_mpi.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/setup_mpi.cmake b/cmake/setup_mpi.cmake index 171c5b20..9031062b 100644 --- a/cmake/setup_mpi.cmake +++ b/cmake/setup_mpi.cmake @@ -1,4 +1,4 @@ -if (MPI_CXX_FOUND) +if (NOT MPI_CXX_FOUND) find_package(MPI) endif () From e5a07ce1e1f673b4cf50ea4792b28107adedb8eb Mon Sep 17 00:00:00 2001 From: Keita Iwabuchi Date: Sun, 14 Apr 2024 16:52:56 -0700 Subject: [PATCH 26/26] Release v0.28 --- CMakeLists.txt | 2 +- docs/Doxyfile.in | 2 +- include/metall/version.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c64eba4..329bcd05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ endif () # Metall general configuration # -------------------------------------------------------------------------------- # project(Metall - VERSION 0.27 + VERSION 0.28 DESCRIPTION "A persistent memory allocator for data-centric analytics" HOMEPAGE_URL "https://github.com/LLNL/metall") diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index fd720cd9..00a76030 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -38,7 +38,7 @@ PROJECT_NAME = "Metall" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = v0.27 +PROJECT_NUMBER = v0.28 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/include/metall/version.hpp b/include/metall/version.hpp index 2db668ba..053831b6 100644 --- a/include/metall/version.hpp +++ b/include/metall/version.hpp @@ -14,7 +14,7 @@ /// METALL_VERSION / 100 % 1000 // the minor version. /// METALL_VERSION % 100 // the patch level. /// \endcode -#define METALL_VERSION 2700 +#define METALL_VERSION 2800 namespace metall { /// \brief Variable type to handle a version data.