diff --git a/CMakeLists.txt b/CMakeLists.txt index 8db05c97..099915b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ endif () # Configuration for building test, benchmark, example, etc. # -------------------------------------------------------------------------------- # cmake_minimum_required(VERSION 3.10) # gtest_discover_tests requires >= CMake 3.10 -project(Metall VERSION 0.6) +project(Metall VERSION 0.7) configure_file(MetallConfig.h.in MetallConfig.h) include_directories(${PROJECT_BINARY_DIR}) diff --git a/README.md b/README.md index f89e606c..d3ace8bd 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Metall (memory allocator for persistent memory) ## Required to Build Metall -- GCC 8.1 or more. +- GCC 8.1 or more (8.3 or more is recommended due to early implementation of the Filesystem library). - Boost C++ Libraries 1.64 or more (build is not required; needs only their header files). diff --git a/bench/adjacency_list/CMakeLists.txt b/bench/adjacency_list/CMakeLists.txt index b8deb07a..73b2214a 100644 --- a/bench/adjacency_list/CMakeLists.txt +++ b/bench/adjacency_list/CMakeLists.txt @@ -6,13 +6,6 @@ else () MESSAGE(STATUS "OpenMP is not found. Use single thread in adjacency_list.") endif () -#if (USE_NUMA_LIB) -# if (${CMAKE_SYSTEM_NAME} MATCHES Linux) -# link_libraries(numa) -# add_definitions(-DMETALL_USE_NUMA_LIB) -# endif() -#endif() - add_executable(run_adj_list_bench_stl run_adj_list_bench_stl.cpp) add_executable(run_adj_list_bench_bip run_adj_list_bench_bip.cpp) add_executable(run_adj_list_bench_bip_extend run_adj_list_bench_bip_extend.cpp) @@ -32,32 +25,5 @@ if (MEMKIND_ROOT) add_executable(run_adj_list_bench_pmem run_adj_list_bench_pmem) endif() -# Experimental version -#if (JEMALLOC_ROOT) -# # set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -# -# add_executable(run_adj_list_bench_metall_jemalloc run_adj_list_bench_metall_jemalloc.cpp) -# -# # Include jemalloc headers -# execute_process(COMMAND ${JEMALLOC_ROOT}/bin/jemalloc-config --includedir -# OUTPUT_VARIABLE JEMALLOC_INCLUDEDIR) -# string(STRIP ${JEMALLOC_INCLUDEDIR} JEMALLOC_INCLUDEDIR) # Remove the newline at the end -# target_include_directories(run_adj_list_bench_metall_jemalloc PUBLIC "${JEMALLOC_INCLUDEDIR}") -# -# # Link to jemalloc library -# execute_process(COMMAND ${JEMALLOC_ROOT}/bin/jemalloc-config --libdir -# OUTPUT_VARIABLE JEMALLOC_LIBDIR) -# string(STRIP ${JEMALLOC_LIBDIR} JEMALLOC_LIBDIR) # Remove the newline at the end -# target_link_directories(run_adj_list_bench_metall_jemalloc PUBLIC "${JEMALLOC_LIBDIR}") -# target_link_libraries(run_adj_list_bench_metall_jemalloc -ljemalloc) -# -# # Libraries required by jemalloc -# execute_process(COMMAND ${JEMALLOC_ROOT}/bin/jemalloc-config --libs -# OUTPUT_VARIABLE JEMALLOC_LIBS) -# string(STRIP ${JEMALLOC_LIBS} JEMALLOC_LIBS) # Remove the newline at the end -# target_compile_options(run_adj_list_bench_metall_jemalloc PUBLIC "${JEMALLOC_LIBS}") -#endif () - - add_subdirectory(edge_generator) add_subdirectory(test) \ No newline at end of file diff --git a/bench/adjacency_list/run_adj_list_bench_metall_jemalloc.cpp b/bench/adjacency_list/run_adj_list_bench_metall_jemalloc.cpp deleted file mode 100644 index 6f3d0c50..00000000 --- a/bench/adjacency_list/run_adj_list_bench_metall_jemalloc.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 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) - -// \brief Use jemalloc as the allocator of the internal data structures -// JEMALLOC_ROOT option must be given to cmake to build this program. -// $ ls ${JEMALLOC_ROOT} -// bin/ include/ lib/ share/ -// Just compile and install jemalloc to get the files - -#include -#include -#include -#include - -#include -#include "../data_structure/multithread_adjacency_list.hpp" -#include "bench_driver.hpp" -#include -#include "../utility/jemalloc_allocator.hpp" - -using namespace adjacency_list_bench; - -using manager_type = metall::v0::basic_manager>; - -using key_type = uint64_t; -using value_type = uint64_t; -using adjacency_list_type = data_structure::multithread_adjacency_list>; - -int main(int argc, char *argv[]) { - - bench_options option; - if (!parse_options(argc, argv, &option)) { - std::abort(); - } - - if (option.segment_file_name.empty()) { - std::cerr << "Datastore path is required" << std::endl; - std::abort(); - } - - { - manager_type manager(metall::create_only, - option.segment_file_name.c_str(), - option.segment_size); - auto adj_list = manager.construct(option.adj_list_key_name.c_str())(manager.get_allocator<>()); - - run_bench(option, adj_list); - - const auto start = utility::elapsed_time_sec(); - manager.flush(); - const auto elapsed_time = utility::elapsed_time_sec(start); - std::cout << "flush_time (s)\t" << elapsed_time << std::endl; - - std::cout << "Writing profile" << std::endl; - manager.profile("/tmp/metall_profile.log"); - } - - return 0; -} \ No newline at end of file diff --git a/bench/adjacency_list/run_adj_list_bench_metall_numa.cpp b/bench/adjacency_list/run_adj_list_bench_metall_numa.cpp deleted file mode 100644 index afc1965c..00000000 --- a/bench/adjacency_list/run_adj_list_bench_metall_numa.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 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 "../data_structure/multithread_adjacency_list.hpp" -#include "../data_structure/partitioned_multithread_adjacency_list.hpp" -#include "../utility/numa_allocator.hpp" -#include "bench_driver.hpp" - -using namespace adjacency_list_bench; -using namespace data_structure; - -using key_type = uint64_t; -using value_type = uint64_t; - -using numa_allocator_type = numa::numa_allocator; -using metall_manager_type = metall::basic_manager; - -using local_adjacency_list_type = multithread_adjacency_list>; -using adjacency_list_type = partitioned_multithread_adjacency_list; - -int main(int argc, char *argv[]) { - - bench_options option; - if (!parse_options(argc, argv, &option)) { - std::abort(); - } - - if (option.datastore_path_list.empty()) { - std::cerr << "Datastore path is required" << std::endl; - std::abort(); - } - - { - std::vector managers; - for (const auto &file_name : option.datastore_path_list) { - managers.emplace_back(new metall_manager_type(metall::create_only, - file_name.c_str(), - option.segment_size, - numa_allocator_type())); - } - - auto adj_list = adjacency_list_type(option.adj_list_key_name, managers.begin(), managers.end()); - - run_bench(option, numa_aware_bench, &adj_list); - - const auto start = metall::detail::utility::elapsed_time_sec(); - for (auto manager : managers) { - manager->flush(); - } - const auto elapsed_time = metall::detail::utility::elapsed_time_sec(start); - std::cout << "flush_time (s)\t" << elapsed_time << std::endl; - - std::cout << "Writing profile" << std::endl; - for (int i = 0; i < (int)option.datastore_path_list.size(); ++i) { - std::cout << "-------------------- [" << i << "] --------------------" << std::endl; - managers[i]->profile(&(std::cout)); - } - } - - return 0; -} \ No newline at end of file diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 2ef292ef..16bcc3ab 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.6 +PROJECT_NUMBER = v0.7 # 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/docs/readthedocs/getting_started/build_and_install.md b/docs/readthedocs/getting_started/build_and_install.md index 8bd75f1a..be511b25 100644 --- a/docs/readthedocs/getting_started/build_and_install.md +++ b/docs/readthedocs/getting_started/build_and_install.md @@ -37,7 +37,7 @@ g++ -std=c++17 your_program.cpp -lstdc++fs ## Required to Build Metall -- GCC 8.1 or more. +- GCC 8.1 or more (8.3 or more is recommended due to early implementation of the Filesystem library). - Boost C++ Libraries 1.64 or more (build is not required; needs only their header files). diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 448f63e8..4a10cffd 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -27,14 +27,6 @@ add_executable(static_mutex static_mutex) add_executable(concurrent_map concurrent_map) -if (USE_NUMA_LIB) - if (${CMAKE_SYSTEM_NAME} MATCHES Linux) - link_libraries(numa) - add_definitions(-DMETALL_USE_NUMA_LIB) - endif() -endif() -add_executable(custom_kernel_allocator custom_kernel_allocator) - if (BUILD_C) add_executable(c_api c_api.c) target_link_libraries(c_api metall_c) diff --git a/example/custom_kernel_allocator.cpp b/example/custom_kernel_allocator.cpp deleted file mode 100644 index 371aaec9..00000000 --- a/example/custom_kernel_allocator.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 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) - -// -------------------------------------------------------------------------------- // -// This is an example of how to use a custom allocator for Metall's principal internal data structures. -// For instance, if you use a numa-aware custom allocator, -// you might be able to achieve better localities for Metall's management data -// -------------------------------------------------------------------------------- // - -#include -#include - -#include -#include "../bench/utility/numa_allocator.hpp" - -using chunk_no_type = uint32_t; -static constexpr std::size_t k_chunk_size = 1UL << 21UL; -using kernel_allocator = numa::numa_allocator; -using manager_type = metall::basic_manager; - -using vector_t = boost::container::vector>; - -int main() { - const std::string manager_path("/tmp/file_path"); - - { - // Construct a manager object giving an object of the numa-aware allocator for Metall's kernel - manager_type manager(metall::create_only, manager_path.c_str(),kernel_allocator()); - - // Allocate and construct a vector object in the persistent memory with a name "vec" - auto pvec = manager.construct("vec")(manager.get_allocator()); - - pvec->push_back(5); // Can use containers normally - } - - // ---------- Assume exit and restart the program at this point ---------- // - - { - // Reload the manager object - manager_type manager(metall::open_only, manager_path.c_str(), kernel_allocator()); - - // Find the previously constructed object - auto pvec = manager.find("vec").first; - - pvec->push_back(10); // Can restart to use containers normally - - std::cout << (*pvec)[0] << std::endl; // Will print "5" - std::cout << (*pvec)[1] << std::endl; // Will print "10" - - manager.destroy("vec"); // Destroy the object - } - - return 0; -} \ No newline at end of file diff --git a/include/metall/basic_manager.hpp b/include/metall/basic_manager.hpp index 8a8c0fb7..6596cdb8 100644 --- a/include/metall/basic_manager.hpp +++ b/include/metall/basic_manager.hpp @@ -20,15 +20,14 @@ 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 -/// \tparam kernel_allocator_type -template > +template class basic_manager { public: // -------------------------------------------------------------------------------- // @@ -36,7 +35,7 @@ class basic_manager { // -------------------------------------------------------------------------------- // /// \brief Manager kernel type - using manager_kernel_type = kernel::manager_kernel; + using manager_kernel_type = kernel::manager_kernel; /// \brief Void pointer type using void_pointer = typename manager_kernel_type::void_pointer; @@ -67,7 +66,7 @@ class basic_manager { // 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: // -------------------------------------------------------------------------------- // @@ -76,39 +75,31 @@ class basic_manager { /// \brief Opens an existing data store. /// \param base_path Path to a data store. - /// \param allocator Allocator to allocate management data. - basic_manager(open_only_t, const char *base_path, - const kernel_allocator_type &allocator = kernel_allocator_type()) - : m_kernel(allocator) { + basic_manager(open_only_t, const char *base_path) + : m_kernel() { m_kernel.open(base_path); } /// \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. - /// \param allocator Allocator to allocate management data. - basic_manager(open_read_only_t, const char *base_path, - const kernel_allocator_type &allocator = kernel_allocator_type()) - : m_kernel(allocator) { + basic_manager(open_read_only_t, const char *base_path) + : m_kernel() { m_kernel.open_read_only(base_path); } /// \brief Creates a new data store (an existing data store will be overwritten). /// \param base_path Path to create a data store. - /// \param allocator Allocator to allocate management data. - basic_manager(create_only_t, const char *base_path, - const kernel_allocator_type &allocator = kernel_allocator_type()) - : m_kernel(allocator) { + basic_manager(create_only_t, const char *base_path) + : m_kernel() { m_kernel.create(base_path); } /// \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. - /// \param allocator Allocator to allocate management data. - basic_manager(create_only_t, const char *base_path, const size_type capacity, - const kernel_allocator_type &allocator = kernel_allocator_type()) - : m_kernel(allocator) { + basic_manager(create_only_t, const char *base_path, const size_type capacity) + : m_kernel() { m_kernel.create(base_path, capacity); } @@ -239,7 +230,7 @@ class basic_manager { /// \return Returns a pointer to the object and the count (if it is not an array, returns 1). /// If not present, nullptr is returned. template - std::pair find(char_ptr_holder_type name) { + std::pair find(char_ptr_holder_type name) const { return m_kernel.template find(name); } @@ -301,7 +292,7 @@ class basic_manager { /// \tparam T Type of the object. /// \return Returns a STL compatible allocator object. template - allocator_type get_allocator() { + allocator_type get_allocator() const { return allocator_type(reinterpret_cast(&(m_kernel.get_segment_header()->manager_kernel_address))); } @@ -363,6 +354,12 @@ class basic_manager { return &m_kernel; } + /// \brief Returns a UUID of the data store. + /// \return UUID in the std::string format; returns an empty string on error. + std::string get_uuid() const { + return m_kernel.get_uuid(); + } + /// \brief Returns a UUID of the data store. /// \param dir_path Path to a data store. /// \return UUID in the std::string format; returns an empty string on error. @@ -370,6 +367,19 @@ class basic_manager { return manager_kernel_type::get_uuid(dir_path); } + /// \brief Gets the version of the Metall that created the backing data store. + /// \return Returns a version number; returns 0 on error. + version_type get_version() const { + return m_kernel.get_version(); + } + + /// \brief Gets the version of the Metall that created 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) { + return manager_kernel_type::get_version(dir_path); + } + // -------------------- For profiling and debug -------------------- // #if !defined(DOXYGEN_SKIP) /// \brief Prints out profiling information. diff --git a/include/metall/detail/utility/file.hpp b/include/metall/detail/utility/file.hpp index 771c06f9..98cbce97 100644 --- a/include/metall/detail/utility/file.hpp +++ b/include/metall/detail/utility/file.hpp @@ -168,6 +168,24 @@ inline bool extend_file_size(const std::string &file_name, const size_t file_siz return ret; } +/// \brief Check if a file, any kinds of file including directory, exists +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); +} + +/// \brief Check if a directory exists +inline bool directory_exist(const std::string &dir_path) { + struct stat statbuf; + if (::stat(dir_path.c_str(), &statbuf) == -1) { + return false; + } + return (uint64_t)S_IFDIR & (uint64_t)(statbuf.st_mode); +} + inline bool create_file(const std::string &file_name) { const int fd = ::open(file_name.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); @@ -183,11 +201,32 @@ inline bool create_file(const std::string &file_name) { } #if defined(__cpp_lib_filesystem) && !defined(METALL_NOT_USE_CXX17_FILESYSTEM_LIB) +/// \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) { + + if (directory_exist(dir_path)) { + return true; + } + bool success = true; try { + std::string 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." +#if (defined(__GNUG__) && !defined(__clang__)) && (__GNUC__ < 8 || (__GNUC__ == 8 && __GNUC_MINOR__ < 3)) // Check if < GCC 8.3 + // Remove trailing separator(s) if they exist: + while (fixed_string.back() == '/') { + fixed_string.pop_back(); + } +#endif + std::error_code ec; - if (!fs::create_directories(dir_path, ec)) { + if (!fs::create_directories(fixed_string, ec)) { + // If the directory exist, create_directories returns false although error_code says 'Success'. logger::out(logger::level::error, __FILE__, __LINE__, ec.message()); success = false; } @@ -198,11 +237,17 @@ 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) { - if (::mkdir(dir_path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == -1) { - return false; + if (directory_exist(dir_path)) { + return true; } - return true; + + std::string mkdir_command("mkdir -p " + dir_path); + const int status = std::system(mkdir_command.c_str()); + return (status != -1) && !!(WIFEXITED(status)); } #endif @@ -224,26 +269,12 @@ inline ssize_t get_file_size(const std::string &file_name) { inline ssize_t get_actual_file_size(const std::string &file_name) { struct stat statbuf; if (::stat(file_name.c_str(), &statbuf) != 0) { - logger::perror(logger::level::error, __FILE__, __LINE__, "stat"); + logger::perror(logger::level::error, __FILE__, __LINE__, "stat (" + file_name + ")"); return -1; } return statbuf.st_blocks * 512LL; } -/// \brief Check if a file, any kinds of file including directory, exists -inline bool file_exist(const std::string &file_name) { - return (::access(file_name.c_str(), F_OK) == 0); -} - -/// \brief Check if a directory exists -inline bool directory_exist(const std::string &dir_path) { - struct stat statbuf; - if (::stat(dir_path.c_str(), &statbuf) == -1) { - return false; - } - return (uint64_t)S_IFDIR & (uint64_t)(statbuf.st_mode); -} - /// \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. diff --git a/include/metall/detail/utility/hash.hpp b/include/metall/detail/utility/hash.hpp index d9be4d29..b9b5fb71 100644 --- a/include/metall/detail/utility/hash.hpp +++ b/include/metall/detail/utility/hash.hpp @@ -78,6 +78,6 @@ struct string_hash { } }; -} // namespace metall::utility +} // namespace metall::detail::utility #endif //METALL_DETAIL_UTILITY_HASH_HPP diff --git a/include/metall/detail/utility/ptree.hpp b/include/metall/detail/utility/ptree.hpp new file mode 100644 index 00000000..87206feb --- /dev/null +++ b/include/metall/detail/utility/ptree.hpp @@ -0,0 +1,145 @@ +// 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) + +#ifndef METALL_DETAIL_UTILITY_PTREE_HPP +#define METALL_DETAIL_UTILITY_PTREE_HPP + +#include +#include + +#include + +namespace metall::detail::utility::ptree { + +namespace { +namespace bptree = boost::property_tree; +} + +using node_type = bptree::ptree; + +inline bool validate_key(const std::string &key) { + using path_type = bptree::ptree::path_type; + return path_type(key).single(); // To avoid confusion, a multi-layer path style string is invalid. +} + +inline bool empty(const node_type &tree) { + return tree.empty(); +} + +inline std::size_t count(const node_type &tree, const std::string &key) { + if (!validate_key(key)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Invalid key: " + key); + return 0; + } + return tree.count(key); +} + +template +inline bool get_value(const node_type &tree, const std::string &key, value_type *const value) { + if (!validate_key(key)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Invalid key: " + key); + return false; + } + + try { + *value = tree.get(key); + } catch (const bptree::ptree_error &e) { + logger::out(logger::level::error, __FILE__, __LINE__, e.what()); + return false; + } + return true; +} + +inline bool get_child(const node_type &tree, const std::string &key, node_type* out) { + if (!validate_key(key)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Invalid key: " + key); + return false; + } + + if (auto child = tree.get_child_optional(key)) { + *out = *child; + return true; + } + return false; +} + +template +inline bool add_value(const std::string &key, const value_type &value, node_type *tree) { + if (!validate_key(key)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Invalid key: " + key); + return false; + } + + try { + tree->add(key, value); + } catch (...) { + logger::out(logger::level::error, __FILE__, __LINE__, "Failed to add: " + key); + return false; + } + return true; +} + +inline bool add_child(const std::string &key, const node_type &child, node_type *tree) { + if (!validate_key(key)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Invalid key: " + key); + return false; + } + + try { + tree->add_child(key, child); + } catch (...) { + logger::out(logger::level::error, __FILE__, __LINE__, "Failed to add: " + key); + return false; + } + return true; +} + +inline bool push_back(const node_type &child, node_type *parent) { + try { + parent->push_back(std::make_pair("", child)); + } catch (...) { + logger::out(logger::level::error, __FILE__, __LINE__, "Failed to pushback an item"); + return false; + } + return true; +} + +inline bool read_json(const std::string &file_name, node_type *root) { + try { + bptree::read_json(file_name, *root); + } catch (const bptree::json_parser_error &e) { + logger::out(logger::level::error, __FILE__, __LINE__, e.what()); + return false; + } + return true; +} + +inline bool write_json(const node_type &root, const std::string &file_name) { + try { + bptree::write_json(file_name, root); + } catch (const bptree::json_parser_error &e) { + logger::out(logger::level::error, __FILE__, __LINE__, e.what()); + return false; + } + return true; +} + +inline bool serialize(const node_type &root, std::string *const out_str) { + try { + bptree::write_json(*out_str, root); + } catch (const bptree::json_parser_error &e) { + logger::out(logger::level::error, __FILE__, __LINE__, e.what()); + return false; + } + return true; +} + +inline std::size_t erase(const std::string &key, node_type *tree) { + return tree->erase(key); +} + +} // namespace metall::detail::utility::ptree + +#endif //METALL_DETAIL_UTILITY_PTREE_HPP diff --git a/include/metall/kernel/bin_directory.hpp b/include/metall/kernel/bin_directory.hpp index 8337e350..e38669b3 100644 --- a/include/metall/kernel/bin_directory.hpp +++ b/include/metall/kernel/bin_directory.hpp @@ -42,7 +42,7 @@ namespace util = metall::detail::utility; /// \tparam _k_num_bins The number of bins /// \tparam _value_type The value type to store /// \tparam _allocator_type The allocator type to allocate internal data -template +template > class bin_directory { public: // -------------------------------------------------------------------------------- // @@ -78,14 +78,14 @@ class bin_directory { // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - explicit bin_directory(const allocator_type &allocator) + explicit bin_directory(const allocator_type &allocator = allocator_type()) : m_table(k_num_bins, bin_type(allocator), allocator) {} - ~bin_directory() = default; + ~bin_directory() noexcept = default; bin_directory(const bin_directory &) = default; - bin_directory(bin_directory &&) = default; + bin_directory(bin_directory &&) noexcept = default; bin_directory &operator=(const bin_directory &) = default; - bin_directory &operator=(bin_directory &&) = default; + bin_directory &operator=(bin_directory &&) noexcept = default; // -------------------------------------------------------------------------------- // // Public methods diff --git a/include/metall/kernel/chunk_directory.hpp b/include/metall/kernel/chunk_directory.hpp index 7a0be5c6..5df418ca 100644 --- a/include/metall/kernel/chunk_directory.hpp +++ b/include/metall/kernel/chunk_directory.hpp @@ -26,7 +26,7 @@ namespace { namespace util = metall::detail::utility; } -template +template class chunk_directory { private: // -------------------------------------------------------------------------------- // @@ -36,8 +36,7 @@ class chunk_directory { static constexpr std::size_t k_max_size = _k_max_size; using bin_no_mngr = bin_number_manager; static constexpr std::size_t k_num_max_slots = k_chunk_size / bin_no_mngr::to_object_size(0); - using multilayer_bitset_type = multilayer_bitset; - using multilayer_bitset_allocator_type = typename multilayer_bitset_type::rebind_allocator_type; + using multilayer_bitset_type = multilayer_bitset; public: // -------------------------------------------------------------------------------- // @@ -53,35 +52,34 @@ class chunk_directory { // Private types and static values // -------------------------------------------------------------------------------- // enum chunk_type : uint8_t { - empty = 0, // This must be 0 + empty = 0, small_chunk = 1, large_chunk_head = 2, - large_chunk_tail = 3 + large_chunk_body = 3 }; + // Chunk directory is just an array of this structure struct entry_type { - entry_type() - : bin_no(), - type(chunk_type::empty), - num_occupied_slots(0), - slot_occupancy() {} - - bin_no_type bin_no; // 1B - chunk_type type; // 1B - slot_count_type num_occupied_slots; // 4B - multilayer_bitset_type slot_occupancy; // 8B + void init() { + type = chunk_type::empty; + num_occupied_slots = 0; + slot_occupancy.init(); + } + + bin_no_type bin_no; // 1 byte + chunk_type type; // 1 byte + slot_count_type num_occupied_slots; // 4 bytes, just for small chunk + multilayer_bitset_type slot_occupancy; // 8 bytes, just for small chunk }; public: // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - explicit chunk_directory(const std::size_t max_num_chunks, - const allocator_type &allocator) + explicit chunk_directory(const std::size_t max_num_chunks) : m_table(nullptr), m_max_num_chunks(0), - m_end_chunk_no(0), - m_multilayer_bitset_allocator(allocator) { + m_begin_unused_chunk_no(0) { priv_allocate(max_num_chunks); } @@ -92,8 +90,8 @@ class chunk_directory { chunk_directory(const chunk_directory &) = delete; chunk_directory &operator=(const chunk_directory &) = delete; - chunk_directory(chunk_directory &&) = default; - chunk_directory &operator=(chunk_directory &&) = default; + chunk_directory(chunk_directory &&) noexcept = default; + chunk_directory &operator=(chunk_directory &&) noexcept = default; // -------------------------------------------------------------------------------- // // Public methods @@ -105,9 +103,9 @@ class chunk_directory { chunk_no_type inserted_chunk_no; if (bin_no < bin_no_mngr::num_small_bins()) { - inserted_chunk_no = insert_small_chunk(bin_no); + inserted_chunk_no = priv_insert_small_chunk(bin_no); } else { - inserted_chunk_no = insert_large_chunk(bin_no); + inserted_chunk_no = priv_insert_large_chunk(bin_no); } assert(inserted_chunk_no < size()); @@ -121,29 +119,29 @@ class chunk_directory { assert(chunk_no < size()); if (empty_chunk(chunk_no)) return; - assert(m_end_chunk_no > 0); + assert(m_begin_unused_chunk_no > 0); if (m_table[chunk_no].type == chunk_type::small_chunk) { const slot_count_type num_slots = slots(chunk_no); m_table[chunk_no].type = chunk_type::empty; m_table[chunk_no].num_occupied_slots = 0; - m_table[chunk_no].slot_occupancy.free(num_slots, m_multilayer_bitset_allocator); + m_table[chunk_no].slot_occupancy.free(num_slots); - if (chunk_no == m_end_chunk_no - 1) { - m_end_chunk_no = static_cast(find_next_used_chunk_backward(chunk_no)) + 1; + if (chunk_no == m_begin_unused_chunk_no - 1) { + m_begin_unused_chunk_no = static_cast(find_next_used_chunk_backward(chunk_no)) + 1; } } else { m_table[chunk_no].type = chunk_type::empty; chunk_no_type offset = 1; - for (; chunk_no + offset < m_max_num_chunks && m_table[chunk_no + offset].type == chunk_type::large_chunk_tail; + for (; chunk_no + offset < m_max_num_chunks && m_table[chunk_no + offset].type == chunk_type::large_chunk_body; ++offset) { m_table[chunk_no + offset].type = chunk_type::empty; } const chunk_no_type last_chunk_no = chunk_no + offset - 1; - if (last_chunk_no == m_end_chunk_no - 1) { - m_end_chunk_no = static_cast(find_next_used_chunk_backward(last_chunk_no)) + 1; + if (last_chunk_no == m_begin_unused_chunk_no - 1) { + m_begin_unused_chunk_no = static_cast(find_next_used_chunk_backward(last_chunk_no)) + 1; } } } @@ -219,8 +217,8 @@ class chunk_directory { /// \brief /// \return std::size_t size() const { - assert(m_end_chunk_no <= m_max_num_chunks); - return m_end_chunk_no; + assert(m_begin_unused_chunk_no <= m_max_num_chunks); + return m_begin_unused_chunk_no; } /// \brief @@ -295,7 +293,7 @@ class chunk_directory { } } else if (m_table[chunk_no].type == chunk_type::large_chunk_head - || m_table[chunk_no].type == chunk_type::large_chunk_tail) { + || m_table[chunk_no].type == chunk_type::large_chunk_body) { ofs << "\n"; if (!ofs) { logger::out(logger::level::critical, @@ -340,8 +338,8 @@ class chunk_directory { m_table[chunk_no].type = chunk_type::small_chunk; } else if (type == static_cast(chunk_type::large_chunk_head)) { m_table[chunk_no].type = chunk_type::large_chunk_head; - } else if (type == static_cast(chunk_type::large_chunk_tail)) { - m_table[chunk_no].type = chunk_type::large_chunk_tail; + } else if (type == static_cast(chunk_type::large_chunk_body)) { + m_table[chunk_no].type = chunk_type::large_chunk_body; } else { logger::out(logger::level::critical, __FILE__, __LINE__, "Invalid chunk type"); return false; @@ -373,7 +371,7 @@ class chunk_directory { } bitset_buf.erase(0, 1); - m_table[chunk_no].slot_occupancy.allocate(num_slots, m_multilayer_bitset_allocator); + m_table[chunk_no].slot_occupancy.allocate(num_slots); if (!m_table[chunk_no].slot_occupancy.deserialize(num_slots, bitset_buf)) { logger::out(logger::level::critical, __FILE__, @@ -383,7 +381,7 @@ class chunk_directory { } } - m_end_chunk_no = std::max(static_cast(chunk_no) + 1, m_end_chunk_no); + m_begin_unused_chunk_no = std::max(static_cast(chunk_no) + 1, m_begin_unused_chunk_no); } if (!ifs.eof()) { @@ -413,12 +411,17 @@ class chunk_directory { } /// \brief Reserves chunk directory. - /// It allocates 'uncommited pages' so that not to waste physical memory until the pages are touched. + /// Allocates 'uncommited pages' so that not to waste physical memory until the pages are touched. + /// Accordingly, this function does not initialize an allocate data. /// \param max_num_chunks bool priv_allocate(const std::size_t max_num_chunks) { assert(!m_table); m_max_num_chunks = max_num_chunks; - /// CAUTION: Assumes that mmap + MAP_ANONYMOUS returns zero-initialized region + + // Assume that mmap + MAP_ANONYMOUS returns 'uncommited pages'. + // An uncommited page will be zero-initialized when it is touched first time; + // however, this class does not relies on that. + // The table entries will be initialized just before they are used. m_table = static_cast(util::map_anonymous_write_mode(nullptr, m_max_num_chunks * sizeof(entry_type))); if (!m_table) { @@ -426,7 +429,8 @@ class chunk_directory { logger::perror(logger::level::critical, __FILE__, __LINE__, "Cannot allocate chunk table"); return false; } - m_end_chunk_no = 0; + + m_begin_unused_chunk_no = 0; return true; } @@ -437,25 +441,30 @@ class chunk_directory { util::os_munmap(m_table, m_max_num_chunks * sizeof(entry_type)); m_table = nullptr; m_max_num_chunks = 0; - m_end_chunk_no = 0; + m_begin_unused_chunk_no = 0; } /// \brief /// \param bin_no /// \param num_slots /// \return - chunk_no_type insert_small_chunk(const bin_no_type bin_no) { + chunk_no_type priv_insert_small_chunk(const bin_no_type bin_no) { const slot_count_type num_slots = calc_num_slots(bin_no_mngr::to_object_size(bin_no)); assert(num_slots > 1); for (chunk_no_type chunk_no = 0; chunk_no < m_max_num_chunks; ++chunk_no) { + if (chunk_no >= m_begin_unused_chunk_no) { + // Initialize (empty) it before just in case + m_table[chunk_no].init(); + } + if (empty_chunk(chunk_no)) { m_table[chunk_no].bin_no = bin_no; m_table[chunk_no].type = chunk_type::small_chunk; m_table[chunk_no].num_occupied_slots = 0; - m_table[chunk_no].slot_occupancy.allocate(num_slots, m_multilayer_bitset_allocator); + m_table[chunk_no].slot_occupancy.allocate(num_slots); - m_end_chunk_no = std::max(static_cast(chunk_no) + 1, m_end_chunk_no); + m_begin_unused_chunk_no = std::max(static_cast(chunk_no) + 1, m_begin_unused_chunk_no); return chunk_no; } @@ -467,28 +476,34 @@ class chunk_directory { /// \brief /// \param bin_no /// \return - chunk_no_type insert_large_chunk(const bin_no_type bin_no) { + chunk_no_type priv_insert_large_chunk(const bin_no_type bin_no) { const std::size_t num_chunks = (bin_no_mngr::to_object_size(bin_no) + k_chunk_size - 1) / k_chunk_size; assert(num_chunks >= 1); - chunk_no_type count_empty_chunks = 0; + chunk_no_type count_continuous_empty_chunks = 0; for (chunk_no_type chunk_no = 0; chunk_no < m_max_num_chunks; ++chunk_no) { + if (chunk_no >= m_begin_unused_chunk_no) { + // Initialize (empty) it before just in case + m_table[chunk_no].init(); + } + if (!empty_chunk(chunk_no)) { - count_empty_chunks = 0; + count_continuous_empty_chunks = 0; continue; } - ++count_empty_chunks; - if (count_empty_chunks == num_chunks) { - const chunk_no_type top_chunk_no = chunk_no - (count_empty_chunks - 1); + + ++count_continuous_empty_chunks; + if (count_continuous_empty_chunks == num_chunks) { + const chunk_no_type top_chunk_no = chunk_no - (count_continuous_empty_chunks - 1); m_table[top_chunk_no].bin_no = bin_no; m_table[top_chunk_no].type = chunk_type::large_chunk_head; for (chunk_no_type offset = 1; offset < num_chunks; ++offset) { m_table[top_chunk_no + offset].bin_no = bin_no; // just in case - m_table[top_chunk_no + offset].type = chunk_type::large_chunk_tail; + m_table[top_chunk_no + offset].type = chunk_type::large_chunk_body; } - m_end_chunk_no = std::max(top_chunk_no + num_chunks, m_end_chunk_no); + m_begin_unused_chunk_no = std::max(top_chunk_no + num_chunks, m_begin_unused_chunk_no); return top_chunk_no; } @@ -515,8 +530,7 @@ class chunk_directory { // -------------------------------------------------------------------------------- // entry_type *m_table; std::size_t m_max_num_chunks; - std::size_t m_end_chunk_no; - multilayer_bitset_allocator_type m_multilayer_bitset_allocator; + std::size_t m_begin_unused_chunk_no; }; } // namespace kernel diff --git a/include/metall/kernel/manager_kernel.hpp b/include/metall/kernel/manager_kernel.hpp index f882e387..f930e026 100644 --- a/include/metall/kernel/manager_kernel.hpp +++ b/include/metall/kernel/manager_kernel.hpp @@ -14,8 +14,11 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -29,7 +32,7 @@ #include #include #include -#include +#include #ifdef METALL_USE_UMAP #include @@ -49,7 +52,7 @@ namespace { namespace util = metall::detail::utility; } -template +template class manager_kernel { public: @@ -65,14 +68,14 @@ class manager_kernel { using chunk_no_type = _chunk_no_type; static constexpr size_type k_chunk_size = _chunk_size; - using internal_data_allocator_type = _internal_data_allocator_type; private: // -------------------------------------------------------------------------------- // // Private types and static values // -------------------------------------------------------------------------------- // - using self_type = manager_kernel<_chunk_no_type, _chunk_size, _internal_data_allocator_type>; - static constexpr const char *k_datastore_dir_name = "metall_datastore"; + 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_core_dir_name = "core"; // For segment static constexpr size_type k_default_vm_reserve_size = METALL_DEFAULT_VM_RESERVE_SIZE; @@ -99,21 +102,24 @@ class manager_kernel { mmap_segment_storage; #endif - static constexpr const char *k_uuid_file_name = "uuid"; - // For actual memory allocation layer static constexpr const char *k_segment_memory_allocator_prefix = "segment_memory_allocator"; using segment_memory_allocator = segment_allocator; + segment_storage_type>; // For named object directory - using named_object_directory_type = named_object_directory; + using named_object_directory_type = named_object_directory; static constexpr const char *k_named_object_directory_prefix = "named_object_directory"; static constexpr const char *k_properly_closed_mark_file_name = "properly_closed_mark"; + // For manager metadata data + static constexpr const char *k_manager_metadata_file_name = "manager_metadata"; + static constexpr const char *k_manager_metadata_key_for_version = "version"; + static constexpr const char *k_manager_metadata_key_for_uuid = "uuid"; + using json_store = metall::detail::utility::ptree::node_type; + #if ENABLE_MUTEX_IN_METALL_MANAGER_KERNEL using mutex_type = util::mutex; using lock_guard_type = util::mutex_lock_guard; @@ -123,7 +129,7 @@ class manager_kernel { // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - explicit manager_kernel(const internal_data_allocator_type &allocator); + manager_kernel(); ~manager_kernel() noexcept; manager_kernel(const manager_kernel &) = delete; @@ -186,7 +192,7 @@ class manager_kernel { /// \param name /// \return template - std::pair find(char_ptr_holder_type name); + std::pair find(char_ptr_holder_type name) const; /// \brief Destroy an already constructed object /// \tparam T @@ -250,10 +256,24 @@ class manager_kernel { static bool consistent(const char *dir_path); /// \brief Returns the UUID of the backing data store. - /// \return UUID in std::string; returns an empty string on error. + /// \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. + /// \return Returns UUID in std::string; returns an empty string on error. static std::string get_uuid(const char *dir_path); - /// \brief Show some profile infromation + /// \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. + /// \return Returns a version number; returns 0 on error. + static version_type get_version(const char *dir_path); + + /// \brief Show some profile information /// \tparam out_stream_type /// \param log_out template @@ -263,13 +283,25 @@ class manager_kernel { // -------------------------------------------------------------------------------- // // Private methods // -------------------------------------------------------------------------------- // - static std::string priv_make_datastore_dir_path(const std::string &base_dir_path); - static std::string priv_make_file_name(const std::string &base_dir_path, const std::string &item_name); + + // Directory structure: + // base_dir_path/ <- this path is given by user + // top_dir/ + // some top-level file + // core_dir/ + // many core files + // many directories + 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_core_dir_path(const std::string &base_dir_path); + static std::string priv_make_core_file_name(const std::string &base_dir_path, const std::string &item_name); static bool priv_init_datastore_directory(const std::string &base_dir_path); bool priv_initialized() const; bool priv_validate_runtime_configuration() const; + static bool priv_consistent(const std::string &base_dir_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); @@ -287,9 +319,6 @@ class manager_kernel { bool priv_allocate_segment_header(void *addr); bool priv_deallocate_segment_header(); - static bool priv_store_uuid(const std::string &base_dir_path); - static std::string priv_restore_uuid(const std::string &base_dir_path); - bool priv_open(const char *base_dir_path, const bool read_only, const size_type vm_reserve_size_request = 0); bool priv_create(const char *base_dir_path, const size_type vm_reserve_size); @@ -304,6 +333,16 @@ class manager_kernel { /// \brief Removes all backing files static bool priv_remove_data_store(const std::string &dir_path); + // ---------------------------------------- Management metadata ---------------------------------------- // + static bool priv_read_management_metadata(const std::string &base_dir_path, json_store* json_root); + static bool priv_write_management_metadata(const std::string &base_dir_path, const json_store& json_root); + + static version_type priv_get_version(const json_store& metadata_json); + static bool priv_set_version(json_store* metadata_json); + + static bool priv_set_uuid(json_store* metadata_json); + static std::string priv_get_uuid(const json_store& metadata_json); + // -------------------------------------------------------------------------------- // // Private fields // -------------------------------------------------------------------------------- // @@ -314,6 +353,7 @@ class manager_kernel { named_object_directory_type m_named_object_directory; segment_storage_type m_segment_storage; segment_memory_allocator m_segment_memory_allocator; + json_store m_manager_metadata; #if ENABLE_MUTEX_IN_METALL_MANAGER_KERNEL mutex_type m_named_object_directory_mutex; diff --git a/include/metall/kernel/manager_kernel_fwd.hpp b/include/metall/kernel/manager_kernel_fwd.hpp index b8704343..b9aa78c6 100644 --- a/include/metall/kernel/manager_kernel_fwd.hpp +++ b/include/metall/kernel/manager_kernel_fwd.hpp @@ -12,8 +12,7 @@ namespace kernel { /// \brief Manager kernel class version 0 /// \tparam chunk_no_type Type of chunk number /// \tparam chunk_size Size of single chunk in byte -/// \tparam allocator_type Allocator used to allocate internal data -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 2b272960..95edd5a0 100644 --- a/include/metall/kernel/manager_kernel_impl.ipp +++ b/include/metall/kernel/manager_kernel_impl.ipp @@ -15,16 +15,16 @@ namespace kernel { // -------------------------------------------------------------------------------- // // Constructor // -------------------------------------------------------------------------------- // -template -manager_kernel:: -manager_kernel(const manager_kernel::internal_data_allocator_type &allocator) +template +manager_kernel::manager_kernel() : m_base_dir_path(), m_vm_region_size(0), m_vm_region(nullptr), m_segment_header(nullptr), - m_named_object_directory(allocator), + m_named_object_directory(), m_segment_storage(), - m_segment_memory_allocator(&m_segment_storage, allocator) + m_segment_memory_allocator(&m_segment_storage), + m_manager_metadata() #if ENABLE_MUTEX_IN_METALL_MANAGER_KERNEL , m_named_object_directory_mutex() #endif @@ -32,54 +32,59 @@ manager_kernel(const manager_kernel::internal_data_al priv_validate_runtime_configuration(); } -template -manager_kernel::~manager_kernel() noexcept { +template +manager_kernel::~manager_kernel() noexcept { close(); - - // This function must be called at the end - priv_mark_properly_closed(m_base_dir_path); } // -------------------------------------------------------------------------------- // // Public methods // -------------------------------------------------------------------------------- // -template -bool manager_kernel::create(const char *base_dir_path, const size_type vm_reserve_size) { +template +bool manager_kernel::create(const char *base_dir_path, const size_type vm_reserve_size) { return priv_create(base_dir_path, vm_reserve_size); } -template -bool manager_kernel::open_read_only(const char *base_dir_path) { +template +bool manager_kernel::open_read_only(const char *base_dir_path) { return priv_open(base_dir_path, true, 0); } -template -bool manager_kernel::open(const char *base_dir_path, +template +bool manager_kernel::open(const char *base_dir_path, const size_type vm_reserve_size_request) { return priv_open(base_dir_path, false, vm_reserve_size_request); } -template -void manager_kernel::close() { +template +void manager_kernel::close() { if (priv_initialized()) { - priv_serialize_management_data(); - m_segment_storage.sync(true); + if (!m_segment_storage.read_only()) { + priv_serialize_management_data(); + m_segment_storage.sync(true); + } + m_segment_storage.destroy(); priv_deallocate_segment_header(); priv_release_vm_region(); + + if (!m_segment_storage.read_only()) { + // This function must be called at the end + priv_mark_properly_closed(m_base_dir_path); + } } } -template -void manager_kernel::flush(const bool synchronous) { +template +void manager_kernel::flush(const bool synchronous) { assert(priv_initialized()); m_segment_storage.sync(synchronous); } -template +template void * -manager_kernel:: -allocate(const manager_kernel::size_type nbytes) { +manager_kernel:: +allocate(const manager_kernel::size_type nbytes) { assert(priv_initialized()); if (m_segment_storage.read_only()) return nullptr; @@ -89,11 +94,11 @@ allocate(const manager_kernel::size_type nbytes) { return static_cast(m_segment_storage.get_segment()) + offset; } -template +template void * -manager_kernel:: -allocate_aligned(const manager_kernel::size_type nbytes, - const manager_kernel::size_type alignment) { +manager_kernel:: +allocate_aligned(const manager_kernel::size_type nbytes, + const manager_kernel::size_type alignment) { assert(priv_initialized()); if (m_segment_storage.read_only()) return nullptr; @@ -112,8 +117,8 @@ allocate_aligned(const manager_kernel::size_type nbyt return addr; } -template -void manager_kernel::deallocate(void *addr) { +template +void manager_kernel::deallocate(void *addr) { assert(priv_initialized()); if (m_segment_storage.read_only()) return; if (!addr) return; @@ -121,35 +126,35 @@ void manager_kernel::deallocate(void *addr) { m_segment_memory_allocator.deallocate(offset); } -template +template template -std::pair::size_type> -manager_kernel::find(char_ptr_holder_type name) { +std::pair::size_type> +manager_kernel::find(char_ptr_holder_type name) const { assert(priv_initialized()); if (name.is_anonymous()) { return std::make_pair(nullptr, 0); } -#if ENABLE_MUTEX_IN_METALL_MANAGER_KERNEL - lock_guard_type guard(m_named_object_directory_mutex); // TODO: don't need at here? -#endif - const char *const raw_name = (name.is_unique()) ? typeid(T).name() : name.get(); - const auto iterator = m_named_object_directory.find(raw_name); - if (iterator == m_named_object_directory.end()) { + if (m_named_object_directory.count(raw_name) == 0) { return std::make_pair(nullptr, 0); } - const auto offset = std::get<1>(iterator->second); - const auto length = std::get<2>(iterator->second); + typename named_object_directory_type::offset_type offset = 0; + typename named_object_directory_type::length_type length = 0; + if (!m_named_object_directory.get_offset(raw_name, &offset) + || !m_named_object_directory.get_length(raw_name, &length)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot get value from named object directory"); + return std::make_pair(nullptr, 0); + } return std::make_pair(reinterpret_cast(offset + static_cast(m_segment_storage.get_segment())), length); } -template +template template -bool manager_kernel::destroy(char_ptr_holder_type name) { +bool manager_kernel::destroy(char_ptr_holder_type name) { assert(priv_initialized()); if (m_segment_storage.read_only()) return false; @@ -165,13 +170,20 @@ bool manager_kernel::destroy(char_ptr_holder_type nam const char *const raw_name = (name.is_unique()) ? typeid(T).name() : name.get(); - const auto iterator = m_named_object_directory.find(raw_name); - if (iterator == m_named_object_directory.end()) return false; // No object with the name + if (m_named_object_directory.count(raw_name) == 0) return false; // No object with the name - const difference_type offset = std::get<1>(iterator->second); - const size_type length = std::get<2>(iterator->second); + typename named_object_directory_type::offset_type offset = 0; + typename named_object_directory_type::length_type length = 0; + if (!m_named_object_directory.get_offset(raw_name, &offset) + || !m_named_object_directory.get_length(raw_name, &length)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot get value from named object directory"); + return false; + } - m_named_object_directory.erase(iterator); + if (m_named_object_directory.erase(raw_name) != 1) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to erase the named object"); + return false; + } // TODO: might be able to free the lock here ? @@ -187,9 +199,9 @@ bool manager_kernel::destroy(char_ptr_holder_type nam return true; } -template +template template -T *manager_kernel::generic_construct(char_ptr_holder_type name, +T *manager_kernel::generic_construct(char_ptr_holder_type name, const size_type num, const bool try2find, const bool dothrow, @@ -206,125 +218,178 @@ T *manager_kernel::generic_construct(char_ptr_holder_ return priv_generic_named_construct(raw_name, num, try2find, dothrow, table); } -template -typename manager_kernel::segment_header_type * -manager_kernel::get_segment_header() const { +template +typename manager_kernel::segment_header_type * +manager_kernel::get_segment_header() const { return reinterpret_cast(m_segment_header); } -template -bool manager_kernel::snapshot(const char *destination_base_dir_path) { +template +bool manager_kernel::snapshot(const char *destination_base_dir_path) { assert(priv_initialized()); m_segment_storage.sync(true); priv_serialize_management_data(); - if (!priv_copy_data_store(m_base_dir_path, destination_base_dir_path, true)) { + if (!util::create_directory(priv_make_top_dir_path(destination_base_dir_path))) { + logger::out(logger::level::error, __FILE__, __LINE__, + "Failed to create directory: " + std::string(destination_base_dir_path)); return false; } - if (!priv_store_uuid(destination_base_dir_path)) { - return false; + // Copy only core directory first + if (!util::clone_file(priv_make_core_dir_path(m_base_dir_path), + priv_make_core_dir_path(destination_base_dir_path),true)) { + std::stringstream ss; + ss << "Failed to copy " << priv_make_top_dir_path(m_base_dir_path) << " to " + << priv_make_top_dir_path(destination_base_dir_path); + logger::out(logger::level::error, __FILE__, __LINE__, ss.str()); } + // Make a new metadata + 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)) return false; + + // Finally, mark it as properly-closed if (!priv_mark_properly_closed(destination_base_dir_path)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create a properly closed mark"); return false; } return true; } -template -bool manager_kernel::copy(const char *source_base_dir_path, +template +bool manager_kernel::copy(const char *source_base_dir_path, const char *destination_base_dir_path) { return priv_copy_data_store(source_base_dir_path, destination_base_dir_path, true); } -template +template std::future -manager_kernel::copy_async(const char *source_dir_path, +manager_kernel::copy_async(const char *source_dir_path, const char *destination_dir_path) { return std::async(std::launch::async, copy, source_dir_path, destination_dir_path); } -template -bool manager_kernel::remove(const char *base_dir_path) { +template +bool manager_kernel::remove(const char *base_dir_path) { return priv_remove_data_store(base_dir_path); } -template -std::future manager_kernel::remove_async(const char *base_dir_path) { +template +std::future manager_kernel::remove_async(const char *base_dir_path) { return std::async(std::launch::async, remove, base_dir_path); } -template -bool manager_kernel::consistent(const char *dir_path) { - return priv_properly_closed(dir_path); +template +bool manager_kernel::consistent(const char *dir_path) { + return priv_consistent(dir_path); } -template -std::string manager_kernel::get_uuid(const char *dir_path) { - return priv_restore_uuid(dir_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 char *dir_path) { + json_store meta_data; + if (!priv_read_management_metadata(dir_path, &meta_data)) { + logger::out(logger::level::error, + __FILE__, + __LINE__, + "Cannot read management metadata in " + std::string(dir_path)); + 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 char *dir_path) { + json_store meta_data; + if (!priv_read_management_metadata(dir_path, &meta_data)) { + logger::out(logger::level::error, + __FILE__, + __LINE__, + "Cannot read management metadata in " + std::string(dir_path)); + return 0; + } + const auto version = priv_get_version(meta_data); + return (version == detail::k_error_version) ? 0 : version; } // -------------------------------------------------------------------------------- // // Private methods // -------------------------------------------------------------------------------- // -template +template std::string -manager_kernel::priv_make_datastore_dir_path(const std::string &base_dir_path) { - return base_dir_path + "/" + k_datastore_dir_name; +manager_kernel::priv_make_top_dir_path(const std::string &base_dir_path) { + return base_dir_path + "/" + k_datastore_top_dir_name; } -template +template std::string -manager_kernel::priv_make_file_name(const std::string &base_dir_path, - const std::string &item_name) { - return priv_make_datastore_dir_path(base_dir_path) + "/" + item_name; +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 +template +std::string +manager_kernel::priv_make_core_dir_path(const std::string &base_dir_path) { + return priv_make_top_dir_path(base_dir_path) + "/" + k_datastore_core_dir_name; +} + +template +std::string +manager_kernel::priv_make_core_file_name(const std::string &base_dir_path, + const std::string &item_name) { + return priv_make_core_dir_path(base_dir_path) + "/" + item_name; +} + +template bool -manager_kernel::priv_init_datastore_directory(const std::string &base_dir_path) { +manager_kernel::priv_init_datastore_directory(const std::string &base_dir_path) { // Create the base directory if needed - if (!util::file_exist(base_dir_path)) { - if (!util::create_directory(base_dir_path)) { - logger::out(logger::level::critical, - __FILE__, - __LINE__, - "Failed to create directory: " + std::string(base_dir_path)); - return false; - } + if (!util::create_directory(base_dir_path)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to create directory: " + base_dir_path); + return false; } + // Remove existing directory to certainly create a new data store if (!remove(base_dir_path.c_str())) { - logger::out(logger::level::critical, - __FILE__, - __LINE__, - "Failed to remove an existing data store: " + std::string(base_dir_path)); + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to remove a directory: " + base_dir_path); return false; } // Create the data store directory if needed - if (!util::create_directory(priv_make_datastore_dir_path(base_dir_path))) { + if (!util::create_directory(priv_make_core_dir_path(base_dir_path))) { logger::out(logger::level::critical, __FILE__, __LINE__, - "Failed to create directory: " + priv_make_datastore_dir_path(base_dir_path)); + "Failed to create directory: " + priv_make_core_dir_path(base_dir_path)); return false; } return true; } -template -bool manager_kernel::priv_initialized() const { +template +bool manager_kernel::priv_initialized() const { assert(!m_base_dir_path.empty()); assert(m_segment_storage.get_segment()); return (m_vm_region && m_vm_region_size > 0 && m_segment_header && m_segment_storage.size() > 0); } -template -bool manager_kernel::priv_validate_runtime_configuration() const { +template +bool manager_kernel::priv_validate_runtime_configuration() const { const auto system_page_size = util::get_page_size(); if (system_page_size <= 0) { logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to get the system page size"); @@ -358,24 +423,36 @@ bool manager_kernel::priv_validate_runtime_configurat return true; } -template -bool manager_kernel::priv_properly_closed(const std::string &base_dir_path) { - return util::file_exist(priv_make_file_name(base_dir_path, k_properly_closed_mark_file_name)); +template +bool manager_kernel::priv_consistent(const std::string &base_dir_path) { + json_store metadata; + return priv_properly_closed(base_dir_path) && (priv_read_management_metadata(base_dir_path, &metadata) + && priv_check_version(metadata)); +} + +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_mark_properly_closed(const std::string &base_dir_path) { - return util::create_file(priv_make_file_name(base_dir_path, k_properly_closed_mark_file_name)); +template +bool manager_kernel::priv_properly_closed(const std::string &base_dir_path) { + return util::file_exist(priv_make_top_level_file_name(base_dir_path, k_properly_closed_mark_file_name)); } -template -bool manager_kernel::priv_unmark_properly_closed(const std::string &base_dir_path) { - return util::remove_file(priv_make_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 util::create_file(priv_make_top_level_file_name(base_dir_path, k_properly_closed_mark_file_name)); } -template +template +bool manager_kernel::priv_unmark_properly_closed(const std::string &base_dir_path) { + return util::remove_file(priv_make_top_level_file_name(base_dir_path, k_properly_closed_mark_file_name)); +} + +template bool -manager_kernel::priv_reserve_vm_region(const size_type nbytes) { +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; @@ -396,9 +473,9 @@ manager_kernel::priv_reserve_vm_region(const size_typ return true; } -template +template bool -manager_kernel::priv_release_vm_region() { +manager_kernel::priv_release_vm_region() { if (!util::munmap(m_vm_region, m_vm_region_size, false)) { logger::out(logger::level::critical, @@ -414,9 +491,9 @@ manager_kernel::priv_release_vm_region() { return true; } -template +template bool -manager_kernel::priv_allocate_segment_header(void *const addr) { +manager_kernel::priv_allocate_segment_header(void *const addr) { if (!addr) { return false; @@ -434,9 +511,9 @@ manager_kernel::priv_allocate_segment_header(void *co return true; } -template +template bool -manager_kernel::priv_deallocate_segment_header() { +manager_kernel::priv_deallocate_segment_header() { m_segment_header->~segment_header_type(); const auto ret = util::munmap(m_segment_header, k_segment_header_size, false); m_segment_header = nullptr; @@ -446,47 +523,10 @@ manager_kernel::priv_deallocate_segment_header() { return ret; } -template -bool manager_kernel::priv_store_uuid(const std::string &base_dir_path) { - std::string file_name = priv_make_file_name(base_dir_path, k_uuid_file_name); - std::ofstream ofs(file_name); - if (!ofs) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to create a file: " + file_name); - return false; - } - ofs << util::uuid(util::uuid_random_generator{}()); - if (!ofs) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot write A UUID to a file: " + file_name); - return false; - } - ofs.close(); - - return true; -} - -template -std::string manager_kernel::priv_restore_uuid(const std::string &base_dir_path) { - std::string file_name = priv_make_file_name(base_dir_path, k_uuid_file_name); - std::ifstream ifs(file_name); - - if (!ifs.is_open()) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to open a file: " + file_name); - return ""; - } - - std::string uuid_string; - if (!(ifs >> uuid_string)) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to read a file: " + file_name); - return ""; - } - - return uuid_string; -} - -template +template template T * -manager_kernel:: +manager_kernel:: priv_generic_named_construct(const char_type *const name, const size_type num, const bool try2find, @@ -498,10 +538,15 @@ priv_generic_named_construct(const char_type *const name, lock_guard_type guard(m_named_object_directory_mutex); // TODO: implement a better lock strategy #endif - const auto iterator = m_named_object_directory.find(name); - if (iterator != m_named_object_directory.end()) { // Found an entry + const auto count = m_named_object_directory.count(name); + assert(count <= 1); + if (count > 0) { // Found an entry if (try2find) { - const auto offset = std::get<1>(iterator->second); + typename named_object_directory_type::offset_type offset = 0; + if (!m_named_object_directory.get_offset(name, &offset)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot get a value from named object directory"); + return nullptr; + } return reinterpret_cast(offset + static_cast(m_segment_storage.get_segment())); } else { return nullptr; @@ -529,15 +574,28 @@ priv_generic_named_construct(const char_type *const name, return static_cast(ptr); } -template -bool manager_kernel::priv_open(const char *base_dir_path, +template +bool manager_kernel::priv_open(const char *base_dir_path, const bool read_only, const size_type vm_reserve_size_request) { if (!priv_validate_runtime_configuration()) { return false; } - if (!consistent(base_dir_path)) { + if (!priv_read_management_metadata(base_dir_path, &m_manager_metadata)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to read management metadata"); + return false; + } + + if (!priv_check_version(m_manager_metadata)) { + std::stringstream ss; + ss << "Invalid version — it was created by Metall v" << to_version_string(priv_get_version(m_manager_metadata)) + << " (currently using v" << to_version_string(METALL_VERSION) << ")"; + logger::out(logger::level::critical, __FILE__, __LINE__, ss.str()); + return false; + } + + if (!priv_properly_closed(base_dir_path)) { logger::out(logger::level::critical, __FILE__, __LINE__, "Inconsistent data store — it was not closed properly and might have been collapsed."); return false; @@ -545,8 +603,9 @@ bool manager_kernel::priv_open(const char *base_dir_p m_base_dir_path = base_dir_path; - const size_type existing_segment_size = segment_storage_type::get_size(priv_make_file_name(m_base_dir_path, - k_segment_prefix)); + const size_type existing_segment_size = segment_storage_type::get_size(priv_make_core_file_name( + m_base_dir_path, + k_segment_prefix)); 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); @@ -571,7 +630,7 @@ bool manager_kernel::priv_open(const char *base_dir_p return false; } - if (!m_segment_storage.open(priv_make_file_name(m_base_dir_path, k_segment_prefix), + if (!m_segment_storage.open(priv_make_core_file_name(m_base_dir_path, k_segment_prefix), m_vm_region_size - k_segment_header_size, static_cast(m_vm_region) + k_segment_header_size, read_only)) { @@ -590,8 +649,8 @@ bool manager_kernel::priv_open(const char *base_dir_p return true; } -template -bool manager_kernel::priv_create(const char *base_dir_path, +template +bool manager_kernel::priv_create(const char *base_dir_path, const size_type vm_reserve_size) { if (!priv_validate_runtime_configuration()) { return false; @@ -624,7 +683,7 @@ bool manager_kernel::priv_create(const char *base_dir return false; } - if (!m_segment_storage.create(priv_make_file_name(m_base_dir_path, k_segment_prefix), + if (!m_segment_storage.create(priv_make_core_file_name(m_base_dir_path, k_segment_prefix), m_vm_region_size - k_segment_header_size, static_cast(m_vm_region) + k_segment_header_size, k_initial_segment_size)) { @@ -634,7 +693,9 @@ bool manager_kernel::priv_create(const char *base_dir return false; } - if (!priv_store_uuid(m_base_dir_path)) { + if (!priv_set_uuid(&m_manager_metadata) || !priv_set_version(&m_manager_metadata) + || !priv_write_management_metadata(m_base_dir_path, m_manager_metadata)) { + m_segment_storage.destroy(); priv_deallocate_segment_header(); priv_release_vm_region(); return false; @@ -644,71 +705,148 @@ bool manager_kernel::priv_create(const char *base_dir } // ---------------------------------------- For serializing/deserializing ---------------------------------------- // -template +template bool -manager_kernel::priv_serialize_management_data() { +manager_kernel::priv_serialize_management_data() { assert(priv_initialized()); if (m_segment_storage.read_only()) return true; - if (!m_named_object_directory.serialize(priv_make_file_name(m_base_dir_path, - k_named_object_directory_prefix).c_str())) { + if (!m_named_object_directory.serialize(priv_make_core_file_name(m_base_dir_path, + k_named_object_directory_prefix).c_str())) { logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to serialize named object directory"); return false; } - if (!m_segment_memory_allocator.serialize(priv_make_file_name(m_base_dir_path, k_segment_memory_allocator_prefix))) { + if (!m_segment_memory_allocator.serialize(priv_make_core_file_name(m_base_dir_path, + k_segment_memory_allocator_prefix))) { return false; } return true; } -template +template bool -manager_kernel::priv_deserialize_management_data() { - if (!m_named_object_directory.deserialize(priv_make_file_name(m_base_dir_path, - k_named_object_directory_prefix).c_str())) { +manager_kernel::priv_deserialize_management_data() { + if (!m_named_object_directory.deserialize(priv_make_core_file_name(m_base_dir_path, + k_named_object_directory_prefix).c_str())) { logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to deserialize named object directory"); return false; } - if (!m_segment_memory_allocator.deserialize(priv_make_file_name(m_base_dir_path, - k_segment_memory_allocator_prefix))) { + if (!m_segment_memory_allocator.deserialize(priv_make_core_file_name(m_base_dir_path, + k_segment_memory_allocator_prefix))) { return false; } return true; } // ---------------------------------------- File operations ---------------------------------------- // -template +template bool -manager_kernel::priv_copy_data_store(const std::string &src_base_dir_path, +manager_kernel::priv_copy_data_store(const std::string &src_base_dir_path, const std::string &dst_base_dir_path, [[maybe_unused]] const bool overwrite) { - const std::string src_datastore_dir_path = priv_make_datastore_dir_path(src_base_dir_path); + const std::string src_datastore_dir_path = priv_make_top_dir_path(src_base_dir_path); if (!util::directory_exist(src_datastore_dir_path)) { logger::out(logger::level::critical, __FILE__, __LINE__, "Source directory does not exist: " + src_datastore_dir_path); return false; } - if (!util::file_exist(dst_base_dir_path)) { - if (!util::create_directory(dst_base_dir_path)) { - logger::out(logger::level::critical, __FILE__, __LINE__, - "Failed to create directory: " + dst_base_dir_path); - return false; - } + if (!util::create_directory(dst_base_dir_path)) { + logger::out(logger::level::critical, __FILE__, __LINE__, + "Failed to create directory: " + dst_base_dir_path); + return false; } - const std::string dst_datastore_dir_path = priv_make_datastore_dir_path(dst_base_dir_path); + const std::string dst_datastore_dir_path = priv_make_top_dir_path(dst_base_dir_path); assert(*(src_datastore_dir_path.end()) != '/'); return util::clone_file(src_datastore_dir_path, dst_datastore_dir_path, true); } -template +template bool -manager_kernel::priv_remove_data_store(const std::string &base_dir_path) { - return util::remove_file(priv_make_datastore_dir_path(base_dir_path)); +manager_kernel::priv_remove_data_store(const std::string &base_dir_path) { + return util::remove_file(priv_make_top_dir_path(base_dir_path)); +} + +// ---------------------------------------- Management metadata ---------------------------------------- // +template +bool manager_kernel::priv_write_management_metadata(const std::string &base_dir_path, + const json_store &json_root) { + + if (!util::ptree::write_json(json_root, + priv_make_core_file_name(base_dir_path, k_manager_metadata_file_name))) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to write management metadata"); + return false; + } + + return true; +} + +template +bool manager_kernel::priv_read_management_metadata(const std::string &base_dir_path, + json_store *json_root) { + if (!util::ptree::read_json(priv_make_core_file_name(base_dir_path, k_manager_metadata_file_name), json_root)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to read management metadata"); + return false; + } + return true; +} + +template +version_type manager_kernel::priv_get_version(const json_store &metadata_json) { + version_type version; + if (!util::ptree::get_value(metadata_json, k_manager_metadata_key_for_version, &version)) { + return detail::k_error_version; + } + return version; +} + +template +bool manager_kernel::priv_set_version(json_store *metadata_json) { + if (util::ptree::count(*metadata_json, k_manager_metadata_key_for_version) > 0) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Version information already exist"); + return false; + } + + if (!util::ptree::add_value(k_manager_metadata_key_for_version, version_type(METALL_VERSION), metadata_json)) { + return false; + } + + return true; +} + +template +std::string manager_kernel::priv_get_uuid(const json_store &metadata_json) { + std::string uuid_string; + if (!util::ptree::get_value(metadata_json, k_manager_metadata_key_for_uuid, &uuid_string)) { + return ""; + } + + return uuid_string; +} + +template +bool manager_kernel::priv_set_uuid(json_store *metadata_json) { + std::stringstream uuid_ss; + uuid_ss << util::uuid(util::uuid_random_generator{}()); + if (!uuid_ss) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to convert UUID to std::string"); + return false; + } + + if (util::ptree::count(*metadata_json, k_manager_metadata_key_for_uuid) > 0) { + logger::out(logger::level::critical, __FILE__, __LINE__, "UUID already exist"); + return false; + } + + if (!util::ptree::add_value(k_manager_metadata_key_for_uuid, uuid_ss.str(), metadata_json)) { + return false; + } + + return true; } } // namespace kernel diff --git a/include/metall/kernel/manager_kernel_profile_impl.ipp b/include/metall/kernel/manager_kernel_profile_impl.ipp index 2a739e0a..830651fd 100644 --- a/include/metall/kernel/manager_kernel_profile_impl.ipp +++ b/include/metall/kernel/manager_kernel_profile_impl.ipp @@ -11,9 +11,9 @@ namespace metall { namespace kernel { -template +template template -void manager_kernel::profile(out_stream_type *log_out) const { +void manager_kernel::profile(out_stream_type *log_out) const { m_segment_memory_allocator.profile(log_out); } diff --git a/include/metall/kernel/multilayer_bitset.hpp b/include/metall/kernel/multilayer_bitset.hpp index 704b1c90..a6fb36ce 100644 --- a/include/metall/kernel/multilayer_bitset.hpp +++ b/include/metall/kernel/multilayer_bitset.hpp @@ -94,7 +94,6 @@ namespace bs = metall::detail::utility::bitset_detail; namespace mlbs = multilayer_bitset_detail; } -template class multilayer_bitset { public: // -------------------------------------------------------------------------------- // @@ -113,9 +112,14 @@ class multilayer_bitset { block_holder() = default; ~block_holder() = default; block_holder(const block_holder &) = default; - block_holder(block_holder &&other) = default; + block_holder(block_holder &&other) noexcept = default; block_holder &operator=(const block_holder &) = default; - block_holder &operator=(block_holder &&other) = default; + block_holder &operator=(block_holder &&other) noexcept = default; + + void init() { + block = 0; + array = nullptr; + } block_type block; // Construct a bitset into this space directly if #of required bits are small block_type *array; // Holds a pointer to a multi-layer bitset table @@ -123,36 +127,42 @@ class multilayer_bitset { using block_type = typename block_holder::block_type; static constexpr std::size_t k_num_bits_in_block = sizeof(block_type) * 8; - using rebind_allocator_type = typename std::allocator_traits::template rebind_alloc; - // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // multilayer_bitset() = default; ~multilayer_bitset() = default; multilayer_bitset(const multilayer_bitset &) = default; - multilayer_bitset(multilayer_bitset &&other) = default; + multilayer_bitset(multilayer_bitset &&other) noexcept = default; multilayer_bitset &operator=(const multilayer_bitset &) = default; - multilayer_bitset &operator=(multilayer_bitset &&other) = default; + multilayer_bitset &operator=(multilayer_bitset &&other) noexcept = default; // -------------------------------------------------------------------------------- // // Public methods // -------------------------------------------------------------------------------- // + + /// \brief Initialize + /// This function does not free memory + void init() { + assert(!m_data.array); + m_data.init(); + } + /// \brief Allocates internal space - void allocate(const std::size_t num_bits, rebind_allocator_type& allocator) { + void allocate(const std::size_t num_bits) { const std::size_t num_bits_power2 = util::next_power_of_2(num_bits); if (num_bits_power2 <= k_num_bits_in_block) { m_data.block = 0; } else { - allocate_multilayer_bitset(num_bits_power2, allocator); + allocate_multilayer_bitset(num_bits_power2); } } /// \brief Users have to explicitly free bitset table - void free(const std::size_t num_bits, rebind_allocator_type& allocator) { + void free(const std::size_t num_bits) { const std::size_t num_bits_power2 = util::next_power_of_2(num_bits); if (k_num_bits_in_block < num_bits_power2) { - free_multilayer_bitset(num_bits_power2, allocator); + free_multilayer_bitset(num_bits_power2); } } @@ -237,9 +247,9 @@ class multilayer_bitset { // Private methods // -------------------------------------------------------------------------------- // // -------------------- Allocation and free -------------------- // - void allocate_multilayer_bitset(const std::size_t num_bits_power2, rebind_allocator_type& allocator) { + void allocate_multilayer_bitset(const std::size_t num_bits_power2) { const std::size_t num_blocks = num_all_blokcs(num_bits_power2); - m_data.array = std::allocator_traits::allocate(allocator, num_blocks); + m_data.array = static_cast(std::malloc(num_blocks * sizeof(block_type))); if (!m_data.array) { logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot allocate multi-layer bitset"); return; @@ -247,8 +257,8 @@ class multilayer_bitset { std::fill(&m_data.array[0], &m_data.array[num_blocks], 0); } - void free_multilayer_bitset(const std::size_t num_bits_power2, rebind_allocator_type& allocator) { - std::allocator_traits::deallocate(allocator, m_data.array, num_all_blokcs(num_bits_power2)); + void free_multilayer_bitset([[maybe_unused]] const std::size_t num_bits_power2) { + std::free(m_data.array); } // -------------------- Find, set, and reset bits -------------------- // diff --git a/include/metall/kernel/named_object_directory.hpp b/include/metall/kernel/named_object_directory.hpp index 496e2ccd..473f7f6e 100644 --- a/include/metall/kernel/named_object_directory.hpp +++ b/include/metall/kernel/named_object_directory.hpp @@ -15,55 +15,63 @@ #include #include -#include #include #include +#include #include +#include +#include namespace metall { namespace kernel { -/// \brief Directory for namaed objects. -/// \tparam offset_type -template +namespace { +namespace util = metall::detail::utility; +namespace json = metall::detail::utility::ptree; +} + +/// \brief Directory for named objects. +/// \tparam _offset_type +/// \tparam _size_type +template class named_object_directory { public: // -------------------------------------------------------------------------------- // // Public types and static values // -------------------------------------------------------------------------------- // + using size_type = _size_type; + using name_type = std::string; + using offset_type = _offset_type; + using length_type = size_type; + using description_type = std::string; private: // -------------------------------------------------------------------------------- // // Private types and static values // -------------------------------------------------------------------------------- // - template - using other_allocator_type = typename std::allocator_traits::template rebind_alloc; - - static constexpr std::size_t k_max_char_size = 1024; - using serialized_string_type = std::array; - using mapped_type = std::tuple; - using key_type = uint64_t; - using value_type = std::pair; - - using table_type = boost::unordered_multimap, - std::equal_to, - other_allocator_type>; - using const_iterator = typename table_type::const_iterator; + using key_string_type = name_type; + + using main_entry_type = std::tuple; + using main_table_type = boost::unordered_map>; + + using key_table_type = boost::unordered_set>; public: + // -------------------------------------------------------------------------------- // + // Public types and static values + // -------------------------------------------------------------------------------- // + using key_iterator = typename key_table_type::const_iterator; + // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - explicit named_object_directory(const allocator_type &allocator) - : m_table(allocator) {} - ~named_object_directory() = default; + named_object_directory() = default; + ~named_object_directory() noexcept = default; named_object_directory(const named_object_directory &) = default; - named_object_directory(named_object_directory &&) = default; + named_object_directory(named_object_directory &&) noexcept = default; named_object_directory &operator=(const named_object_directory &) = default; - named_object_directory &operator=(named_object_directory &&) = default; + named_object_directory &operator=(named_object_directory &&) noexcept = default; // -------------------------------------------------------------------------------- // // Public methods @@ -72,91 +80,141 @@ class named_object_directory { /// \param name /// \param offset /// \param length + /// \param description /// \return - bool insert(const std::string &name, const offset_type offset, const size_type length) { - serialized_string_type serialized_name; - if (!serialize_string(name, &serialized_name)) { + bool insert(const name_type &name, + const offset_type offset, + const length_type length, + const description_type &description = std::string()) { + bool inserted = false; + + try { + inserted = m_main_table.emplace(name, std::make_tuple(name, offset, length, description)).second; + if (inserted != m_key_table.insert(name).second) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Error occurred in an internal table"); + return false; + } + } catch (...) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Exception was thrown"); return false; } - if (find(name) != end()) return false; // Duplicate element exists + return inserted; + } - m_table.emplace(hash_string(name), std::make_tuple(serialized_name, offset, length)); - return true; + /// \brief + /// \param name + /// \return + size_type count(const name_type &name) const { + assert(m_main_table.count(name) == m_key_table.count(name)); + return m_main_table.count(name); + } + + /// \brief + /// \return + key_iterator keys_begin() const { + return m_key_table.cbegin(); + } + + /// \brief + /// \return + key_iterator keys_end() const { + return m_key_table.cend(); } /// \brief /// \param name + /// \param buf /// \return - const_iterator find(const std::string &name) const { - auto itr = m_table.find(hash_string(name)); + bool get_offset(const name_type &name, offset_type *buf) const { + auto itr = m_main_table.find(name); + if (itr == m_main_table.end()) return false; - serialized_string_type serialized_name; - if (!serialize_string(name, &serialized_name)) { - return m_table.end(); - } + *buf = std::get(itr->second); + return true; + } - for (; itr != m_table.end(); ++itr) { - if (serialized_name == std::get<0>(itr->second)) { - return itr; - } - } + /// \brief + /// \param name + /// \param buf + /// \return + bool get_length(const name_type &name, length_type *buf) const { + auto itr = m_main_table.find(name); + if (itr == m_main_table.end()) return false; - return m_table.end(); + *buf = std::get(itr->second); + return true; } /// \brief + /// \param name + /// \param buf /// \return - const_iterator begin() const { - return m_table.begin(); + bool get_description(const name_type &name, description_type *buf) const { + auto itr = m_main_table.find(name); + if (itr == m_main_table.end()) return false; + + *buf = std::get(itr->second); + return true; } /// \brief + /// \param name + /// \param description /// \return - const_iterator end() const { - return m_table.end(); + bool set_description(const name_type &name, const description_type &description) { + auto itr = m_main_table.find(name); + if (itr == m_main_table.end()) return false; + + std::get(itr->second) = description; + return true; } /// \brief /// \param name /// \return - void erase(const const_iterator position) { - m_table.erase(position); + size_type erase(const name_type &name) { + size_type num_erased = 0; + try { + num_erased = m_key_table.erase(name); + if (m_main_table.erase(name) != num_erased) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Error occurred in an internal table"); + num_erased = 0; + } + } catch (...) { + return 0; + } + return num_erased; } /// \brief /// \param path bool serialize(const char *const path) const { - std::ofstream ofs(path); - if (!ofs.is_open()) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot open: " + std::string(path)); - return false; - } - for (const auto &item : m_table) { - std::stringstream ss; - - ss << item.first; // Key - - // Serialized name - const auto &serialized_name = std::get<0>(item.second); - for (std::size_t i = 0; i < serialized_name.size(); ++i) { - ss << " " << static_cast(serialized_name[i]); + json::node_type json_named_objects_list; + for (const auto &item : m_main_table) { + json::node_type json_named_object_entry; + if (!json::add_value(json_key::name, std::get(item.second), &json_named_object_entry) || + !json::add_value(json_key::offset, std::get(item.second), &json_named_object_entry) || + !json::add_value(json_key::length, std::get(item.second), &json_named_object_entry) || + !json::add_value(json_key::description, + std::get(item.second), + &json_named_object_entry)) { + return false; } - - ss << " " << static_cast(std::get<1>(item.second)) // Offset - << " " << static_cast(std::get<2>(item.second)); // Length - - ofs << ss.str() << "\n"; - if (!ofs) { - logger::out(logger::level::critical, - __FILE__, - __LINE__, - "Something happened in the ofstream: " + std::string(path)); + if (!json::push_back(json_named_object_entry, &json_named_objects_list)) { return false; } } - ofs.close(); + + json::node_type json_root; + if (!json::add_child(json_key::named_objects, json_named_objects_list, &json_root)) { + return false; + } + + if (!json::write_json(json_root, path)) { + return false; + } return true; } @@ -164,58 +222,34 @@ class named_object_directory { /// \brief /// \param path bool deserialize(const char *const path) { - std::ifstream ifs(path); - if (!ifs.is_open()) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Cannot open: " + std::string(path)); + json::node_type json_root; + if (!json::read_json(path, &json_root)) { return false; } - std::size_t count = 0; - const std::size_t num_reads_per_item = 1 + std::tuple_size::value + 2; - uint64_t buf; - - key_type key = key_type(); - serialized_string_type serialized_name = serialized_string_type(); - offset_type offset = 0; - size_type length = 0; - while (ifs >> buf) { - if (count == 0) { - key = buf; - } else if (1 <= count && count < num_reads_per_item - 2) { - serialized_name[count - 1] = static_cast(buf); - } else if (count == num_reads_per_item - 2) { - offset = buf; - } else if (count == num_reads_per_item - 1) { - length = buf; - - const std::string name = deserialize_string(serialized_name); - if (key != hash_string(name)) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Something is wrong in the read data"); - return false; - } - - const bool ret = insert(name, offset, length); - if (!ret) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to insert"); - return false; - } - } else { - assert(false); - } - - ++count; - count %= num_reads_per_item; - } - - if (!ifs.eof()) { - logger::out(logger::level::critical, - __FILE__, - __LINE__, - "Something happened in the ifstream: " + std::string(path)); + json::node_type json_named_objects_list; + if (!json::get_child(json_root, json_key::named_objects, &json_named_objects_list)) { return false; } - ifs.close(); + for (const auto &object : json_named_objects_list) { + name_type name; + offset_type offset = 0; + length_type length = 0; + description_type description; + + if (!json::get_value(object.second, json_key::name, &name) || + !json::get_value(object.second, json_key::offset, &offset) || + !json::get_value(object.second, json_key::length, &length) || + !json::get_value(object.second, json_key::description, &description)) { + return false; + } + + if (!insert(name, offset, length, description)) { + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to reconstruct named object table"); + return false; + } + } return true; } @@ -224,38 +258,37 @@ class named_object_directory { // -------------------------------------------------------------------------------- // // Private types and static values // -------------------------------------------------------------------------------- // + enum index { + name = 0, + offset = 1, + length = 2, + description = 3 + }; + + // JSON structure + // { + // "named_objects" : [ + // {"name" : "object0", "offset" : 0x845, "length" : 1, "description" : ""}, + // {"name" : "object1", "offset" : 0x432, "length" : 2, "description" : "..."} + // ] + // } + struct json_key { + static constexpr const char *named_objects = "named_objects"; + static constexpr const char *name = "name"; + static constexpr const char *offset = "offset"; + static constexpr const char *length = "length"; + static constexpr const char *description = "description"; + }; // -------------------------------------------------------------------------------- // // Private methods // -------------------------------------------------------------------------------- // - key_type hash_string(const std::string &name) const { - return std::hash()(std::string(name)); - } - bool serialize_string(const std::string &name, serialized_string_type *out) const { - if (name.size() > std::tuple_size::value) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Too long name: " + name); - return false; - } - out->fill('\0'); - for (std::size_t i = 0; i < name.size(); ++i) { - (*out)[i] = name[i]; - } - return true; - } - - std::string deserialize_string(const serialized_string_type &serialized_string) const { - std::string str; - for (const auto &c : serialized_string) { - if (c == '\0') break; - str.push_back(c); - } - return str; - } // -------------------------------------------------------------------------------- // // Private fields // -------------------------------------------------------------------------------- // - table_type m_table; + main_table_type m_main_table; + key_table_type m_key_table; }; } // namespace kernel diff --git a/include/metall/kernel/object_cache.hpp b/include/metall/kernel/object_cache.hpp index dc6c20a7..808b68d6 100644 --- a/include/metall/kernel/object_cache.hpp +++ b/include/metall/kernel/object_cache.hpp @@ -11,7 +11,10 @@ #include #include #include +#include + #include + #include #include #include @@ -27,7 +30,7 @@ namespace { namespace util = metall::detail::utility; } -template +template class object_cache { public: // -------------------------------------------------------------------------------- // @@ -36,23 +39,20 @@ class object_cache { static constexpr unsigned int k_num_bins = _k_num_bins; static constexpr unsigned int k_full_cache_size = 8; using difference_type = _difference_type; - using allocator_type = _allocator_type; + using bin_no_manager = _bin_no_manager; private: // -------------------------------------------------------------------------------- // // Private types and static values // -------------------------------------------------------------------------------- // - template - using other_allocator_type = typename std::allocator_traits::template rebind_alloc; - using local_object_cache_type = bin_directory<_k_num_bins, difference_type, allocator_type>; - using cache_table_allocator = boost::container::scoped_allocator_adaptor>; - using cache_table_type = boost::container::vector; + using local_object_cache_type = bin_directory<_k_num_bins, difference_type>; + using cache_table_type = boost::container::vector; static constexpr unsigned int k_num_cache_per_core = 4; static constexpr std::size_t k_max_total_cache_size_per_bin = 1ULL << 20ULL; static constexpr unsigned int k_cache_block_size = 8; // Add and remove caches by this size static constexpr std::size_t k_max_cache_object_size = k_max_total_cache_size_per_bin / k_cache_block_size / 2; - static constexpr unsigned int k_max_bin_no = bin_no_mngr::to_bin_no(k_max_cache_object_size); + static constexpr unsigned int k_max_bin_no = _bin_no_manager::to_bin_no(k_max_cache_object_size); static constexpr int k_cpu_core_no_cache_duration = 4; #if ENABLE_MUTEX_IN_METALL_OBJECT_CACHE @@ -70,18 +70,18 @@ class object_cache { // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - object_cache(const allocator_type &allocator) - : m_cache_table(num_cores() * k_num_cache_per_core, allocator) + object_cache() + : m_cache_table(num_cores() * k_num_cache_per_core) #if ENABLE_MUTEX_IN_METALL_OBJECT_CACHE , m_mutex(m_cache_table.size()) #endif - {}; + {} - ~object_cache() = default; + ~object_cache() noexcept = default; object_cache(const object_cache &) = default; - object_cache(object_cache &&) = default; + object_cache(object_cache &&) noexcept = default; object_cache &operator=(const object_cache &) = default; - object_cache &operator=(object_cache &&) = default; + object_cache &operator=(object_cache &&) noexcept = default; // -------------------------------------------------------------------------------- // // Public methods @@ -126,7 +126,7 @@ class object_cache { #endif m_cache_table[cache_no].insert(bin_no, object_offset); - const auto object_size = bin_no_mngr::to_object_size(bin_no); + const auto object_size = _bin_no_manager::to_object_size(bin_no); if (m_cache_table[cache_no].size(bin_no) * object_size >= k_max_total_cache_size_per_bin) { assert(m_cache_table[cache_no].size(bin_no) >= k_cache_block_size); difference_type offsets[k_cache_block_size]; diff --git a/include/metall/kernel/segment_allocator.hpp b/include/metall/kernel/segment_allocator.hpp index d108109a..469f4e30 100644 --- a/include/metall/kernel/segment_allocator.hpp +++ b/include/metall/kernel/segment_allocator.hpp @@ -39,9 +39,10 @@ namespace { namespace util = metall::detail::utility; } -template +template class segment_allocator { public: // -------------------------------------------------------------------------------- // @@ -52,7 +53,6 @@ class segment_allocator { static constexpr size_type k_max_size = _max_size; static constexpr difference_type k_null_offset = std::numeric_limits::max(); using segment_storage_type = _segment_storage_type; - using internal_data_allocator_type = _internal_data_allocator_type; private: // -------------------------------------------------------------------------------- // @@ -67,20 +67,17 @@ class segment_allocator { // For non-full chunk number bin (used to called 'bin directory') // NOTE: we only manage the non-full chunk numbers of the small bins (small oject sizes) - using non_full_chunk_bin_type = bin_directory; + using non_full_chunk_bin_type = bin_directory; static constexpr const char *k_non_full_chunk_bin_file_name = "non_full_chunk_bin"; // For chunk directory - using chunk_directory_type = chunk_directory; + using chunk_directory_type = chunk_directory; using chunk_slot_no_type = typename chunk_directory_type::slot_no_type; static constexpr const char *k_chunk_directory_file_name = "chunk_directory"; // For object cache #ifndef METALL_DISABLE_OBJECT_CACHE - using small_object_cache_type = object_cache; + using small_object_cache_type = object_cache; #endif #if ENABLE_MUTEX_IN_METALL_SEGMENT_ALLOCATOR @@ -92,13 +89,12 @@ class segment_allocator { // -------------------------------------------------------------------------------- // // Constructor & assign operator // -------------------------------------------------------------------------------- // - explicit segment_allocator(segment_storage_type *segment_storage, - const internal_data_allocator_type &allocator = internal_data_allocator_type()) - : m_non_full_chunk_bin(allocator), - m_chunk_directory(k_max_size / k_chunk_size, allocator), + explicit segment_allocator(segment_storage_type *segment_storage) + : m_non_full_chunk_bin(), + m_chunk_directory(k_max_size / k_chunk_size), m_segment_storage(segment_storage) #ifndef METALL_DISABLE_OBJECT_CACHE - , m_object_cache(allocator) + , m_object_cache() #endif #if ENABLE_MUTEX_IN_METALL_SEGMENT_ALLOCATOR , m_chunk_mutex(), @@ -111,8 +107,8 @@ class segment_allocator { segment_allocator(const segment_allocator &) = delete; segment_allocator &operator=(const segment_allocator &) = delete; - segment_allocator(segment_allocator &&) = default; - segment_allocator &operator=(segment_allocator &&) = default; + segment_allocator(segment_allocator &&) noexcept = default; + segment_allocator &operator=(segment_allocator &&) noexcept = default; public: // -------------------------------------------------------------------------------- // diff --git a/include/metall/kernel/segment_storage/mmap_segment_storage.hpp b/include/metall/kernel/segment_storage/mmap_segment_storage.hpp index 7b2c3107..05845f5a 100644 --- a/include/metall/kernel/segment_storage/mmap_segment_storage.hpp +++ b/include/metall/kernel/segment_storage/mmap_segment_storage.hpp @@ -190,7 +190,7 @@ class mmap_segment_storage { m_block_size, m_current_segment_size, read_only)) { - logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to map a file " + m_block_size); + logger::out(logger::level::critical, __FILE__, __LINE__, "Failed to map a file " + std::to_string(m_block_size)); return false; } m_current_segment_size += m_block_size; diff --git a/include/metall/metall.hpp b/include/metall/metall.hpp index ff6b9159..c4bd12b8 100644 --- a/include/metall/metall.hpp +++ b/include/metall/metall.hpp @@ -25,9 +25,6 @@ using manager = basic_manager<>; /// \example complex_map.cpp /// This is an example of how to use a complex STL map container with Metall. -/// \example custom_kernel_allocator.cpp -/// This is an example of how to use a custom allocator for Metall kernel. - /// \example multilevel_containers.cpp /// This is an example of how to use a multi-level STL container with Metall. diff --git a/include/metall/version.hpp b/include/metall/version.hpp index ede597c9..5ab256ac 100644 --- a/include/metall/version.hpp +++ b/include/metall/version.hpp @@ -9,12 +9,41 @@ #define METALL_VERSION_HPP /// \brief Metall version. +/// Metall follows the Semantic Versioning 2.0.0. /// \details /// \code /// METALL_VERSION / 100000 // the major version. /// METALL_VERSION / 100 % 1000 // the minor version. /// METALL_VERSION % 100 // the patch level. /// \endcode -#define METALL_VERSION 000600 +#define METALL_VERSION 700 + +namespace metall { +/// \brief Variable type to handle a version data. +using version_type = uint32_t; +static_assert(std::numeric_limits::max() >= METALL_VERSION, + "version_type cannot handle the current version"); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace detail { +static constexpr version_type k_error_version = 0; +static_assert(k_error_version != METALL_VERSION, "The current version is equal to a error number"); +} +#endif // DOXYGEN_SHOULD_SKIP_THIS + +/// \brief Converts a raw version number to a std::string with format 'MAJOR.MINOR.PATCH'. +/// \details +/// \code +/// // Example usage +/// std::string ver_string = to_version_string(METALL_VERSION); +/// \endcode +/// \param version A version number. +/// \return A version string. +inline std::string to_version_string(const version_type version) { + return std::to_string(version / 100000) + + "." + std::to_string(version / 100 % 1000) + + "." + std::to_string(version % 100); +} +} #endif //METALL_VERSION_HPP diff --git a/include/metall_utility/metall_mpi_adaptor.hpp b/include/metall_utility/metall_mpi_adaptor.hpp index 16d572fa..5aa2d704 100644 --- a/include/metall_utility/metall_mpi_adaptor.hpp +++ b/include/metall_utility/metall_mpi_adaptor.hpp @@ -225,35 +225,38 @@ class metall_mpi_adaptor { // Private methods // -------------------------------------------------------------------------------- // static void priv_setup_root_dir(const std::string &root_dir_prefix, const MPI_Comm &comm) { - if (priv_mpi_comm_rank(comm) == priv_determine_local_root_rank(comm)) { // Only one process creates at each node - const std::string root_dir_path = priv_make_root_dir_path(root_dir_prefix); + const int rank = priv_mpi_comm_rank(comm); + const int size = priv_mpi_comm_size(comm); + const std::string root_dir_path = priv_make_root_dir_path(root_dir_prefix); - if (metall::detail::utility::file_exist(root_dir_path)) { // We must create a directory + // Make sure the root directory and a file with the same name do not exist + const auto local_ret = metall::detail::utility::file_exist(root_dir_path); + if (priv_global_or(local_ret, comm)) { + if (rank == 0) { logger::out(logger::level::error, __FILE__, __LINE__, - "Root directory already exists: " + root_dir_path); + "Root directory (or a file with the same name) already exists: " + root_dir_path); ::MPI_Abort(comm, -1); - } else { - logger::perror(logger::level::verbose, __FILE__, __LINE__, "fstat"); - - std::stringstream ss; - ss << "Rank " << priv_mpi_comm_rank(comm) << " is creating a root directory: " << root_dir_path; - logger::out(logger::level::info, __FILE__, __LINE__, ss.str()); + } + } + priv_mpi_barrier(comm); + for (int i = 0; i < size; ++i) { + if (i == rank && !metall::detail::utility::directory_exist(root_dir_path)) { if (!metall::detail::utility::create_directory(root_dir_path)) { logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create directory: " + root_dir_path); ::MPI_Abort(comm, -1); } - } - const std::string mark_file = root_dir_path + "/" + k_datastore_mark_file_name; - if (!metall::detail::utility::create_file(mark_file)) { - logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create file: " + mark_file); - ::MPI_Abort(comm, -1); - } + const std::string mark_file = root_dir_path + "/" + k_datastore_mark_file_name; + if (!metall::detail::utility::create_file(mark_file)) { + logger::out(logger::level::error, __FILE__, __LINE__, "Failed to create file: " + mark_file); + ::MPI_Abort(comm, -1); + } - priv_store_partition_size(root_dir_prefix, comm); + priv_store_partition_size(root_dir_prefix, comm); + } + priv_mpi_barrier(comm); } - priv_mpi_barrier(comm); } static void priv_store_partition_size(const std::string &root_dir_prefix, const MPI_Comm &comm) { @@ -340,6 +343,14 @@ class metall_mpi_adaptor { return ret.second; } + static bool priv_global_or(const bool local_result, const MPI_Comm &comm) { + const auto ret = mpi::global_logical_or(local_result, comm); + if (!ret.first) { + ::MPI_Abort(comm, -1); + } + return ret.second; + } + static int priv_determine_local_root_rank(const MPI_Comm &comm) { const int rank = mpi::determine_local_root(comm); if (rank == -1) { diff --git a/test/container/concurrent_map_test.cpp b/test/container/concurrent_map_test.cpp index e5d23cca..98d68a8f 100644 --- a/test/container/concurrent_map_test.cpp +++ b/test/container/concurrent_map_test.cpp @@ -126,11 +126,9 @@ TEST (ConcurrentMapTest, Persistence) { using allocator_type = bip::allocator, bip::managed_mapped_file::segment_manager>; using map_type = metall::container::concurrent_map, std::hash, allocator_type, 2>; - const std::string dir_path(test_utility::make_test_dir_path("concurrent_map")); - const std::string file_path(dir_path + "/persistentce_test"); + const std::string file_path(test_utility::make_test_path()); test_utility::create_test_dir(); - metall::detail::utility::create_directory(dir_path); metall::detail::utility::remove_file(file_path); std::vector> inputs(10); diff --git a/test/container/fallback_allocator_adaptor_test.cpp b/test/container/fallback_allocator_adaptor_test.cpp index f228fa80..8fd42e3c 100644 --- a/test/container/fallback_allocator_adaptor_test.cpp +++ b/test/container/fallback_allocator_adaptor_test.cpp @@ -18,7 +18,7 @@ template using fb_alloc_type = metall::utility::fallback_allocator_adaptor>; const std::string &dir_path() { - const static std::string path(test_utility::make_test_dir_path("FallbackAllocatorAdaptorTest")); + const static std::string path(test_utility::make_test_path()); return path; } diff --git a/test/container/stl_allocator_test.cpp b/test/container/stl_allocator_test.cpp index 3a04a364..865d2d48 100644 --- a/test/container/stl_allocator_test.cpp +++ b/test/container/stl_allocator_test.cpp @@ -17,7 +17,7 @@ template using alloc_type = metall::manager::allocator_type; const std::string &dir_path() { - const static std::string path(test_utility::make_test_dir_path("StlAllocatorTest")); + const static std::string path(test_utility::make_test_path()); return path; } diff --git a/test/kernel/bin_directory_test.cpp b/test/kernel/bin_directory_test.cpp index 5cdfa2e8..d84c919f 100644 --- a/test/kernel/bin_directory_test.cpp +++ b/test/kernel/bin_directory_test.cpp @@ -95,12 +95,15 @@ TEST(BinDirectoryTest, Serialize) { obj.insert(num_small_bins - 1, 3); obj.insert(num_small_bins - 1, 4); - ASSERT_TRUE(test_utility::create_test_dir()); - const auto file = test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name()); + test_utility::create_test_dir(); + const auto file = test_utility::make_test_path(); ASSERT_TRUE(obj.serialize(file.c_str())); } TEST(BinDirectoryTest, Deserialize) { + test_utility::create_test_dir(); + const auto file = test_utility::make_test_path(); + { std::allocator allocator; directory_type obj(allocator); @@ -110,14 +113,12 @@ TEST(BinDirectoryTest, Deserialize) { obj.insert(num_small_bins - 1, 3); obj.insert(num_small_bins - 1, 4); - const auto file = test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name()); obj.serialize(file.c_str()); } { std::allocator allocator; directory_type obj(allocator); - const auto file = test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name()); ASSERT_TRUE(obj.deserialize(file.c_str())); #ifdef METALL_USE_SPACE_AWARE_BIN diff --git a/test/kernel/chunk_directory_test.cpp b/test/kernel/chunk_directory_test.cpp index 25ba7387..e73a78ff 100644 --- a/test/kernel/chunk_directory_test.cpp +++ b/test/kernel/chunk_directory_test.cpp @@ -19,13 +19,11 @@ using bin_no_mngr = metall::kernel::bin_number_manager constexpr int k_num_small_bins = bin_no_mngr::num_small_bins(); using chunk_directory_type = metall::kernel::chunk_directory>; + k_chunk_size, + 1ULL << 48>; TEST(ChunkDirectoryTest, InsertSmallChunk) { - std::allocator allocator; - chunk_directory_type directory(k_num_small_bins, allocator); + chunk_directory_type directory(k_num_small_bins); for (uint32_t i = 0; i < k_num_small_bins - 1; ++i) { auto bin_no = static_cast(i); @@ -34,8 +32,7 @@ TEST(ChunkDirectoryTest, InsertSmallChunk) { } TEST(ChunkDirectoryTest, InsertLargeChunk) { - std::allocator allocator; - chunk_directory_type directory(1 << 20, allocator); + chunk_directory_type directory(1 << 20); std::size_t offset = 0; for (uint32_t i = k_num_small_bins; i < k_num_small_bins + 10; ++i) { @@ -46,8 +43,7 @@ TEST(ChunkDirectoryTest, InsertLargeChunk) { } TEST(ChunkDirectoryTest, MarkSlot) { - std::allocator allocator; - chunk_directory_type directory(bin_no_mngr::num_small_bins() + 1, allocator); + chunk_directory_type directory(bin_no_mngr::num_small_bins() + 1); for (uint32_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { auto bin_no = static_cast(i); @@ -69,8 +65,7 @@ TEST(ChunkDirectoryTest, MarkSlot) { } TEST(ChunkDirectoryTest, UnmarkSlot) { - std::allocator allocator; - chunk_directory_type directory(bin_no_mngr::num_small_bins() + 1, allocator); + chunk_directory_type directory(bin_no_mngr::num_small_bins() + 1); for (uint32_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { auto bin_no = static_cast(i); @@ -94,8 +89,7 @@ TEST(ChunkDirectoryTest, UnmarkSlot) { } TEST(ChunkDirectoryTest, Serialize) { - std::allocator allocator; - chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4, allocator); + chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4); for (uint32_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { auto bin_no = static_cast(i); @@ -104,18 +98,17 @@ TEST(ChunkDirectoryTest, Serialize) { directory.insert(bin_no_mngr::num_small_bins()); // 1 chunk directory.insert(bin_no_mngr::num_small_bins() + 1); // 2 chunks - ASSERT_TRUE(test_utility::create_test_dir()); - const auto file(test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + test_utility::create_test_dir(); + const auto file(test_utility::make_test_path()); ASSERT_TRUE(directory.serialize(file.c_str())); } TEST(ChunkDirectoryTest, Deserialize) { ASSERT_TRUE(test_utility::create_test_dir()); - const auto file(test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + const auto file(test_utility::make_test_path()); { - std::allocator allocator; - chunk_directory_type directory(bin_no_mngr::num_small_bins() + 5, allocator); + chunk_directory_type directory(bin_no_mngr::num_small_bins() + 5); for (typename bin_no_mngr::bin_no_type bin_no = 0; bin_no < bin_no_mngr::num_small_bins(); ++bin_no) { chunk_no_type new_chunk_no = directory.insert(bin_no); @@ -131,8 +124,7 @@ TEST(ChunkDirectoryTest, Deserialize) { } { - std::allocator allocator; - chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4, allocator); + chunk_directory_type directory(bin_no_mngr::num_small_bins() + 4); ASSERT_TRUE(directory.deserialize(file.c_str())); for (uint64_t i = 0; i < bin_no_mngr::num_small_bins(); ++i) { diff --git a/test/kernel/copy_file_test.cpp b/test/kernel/copy_file_test.cpp index 9789e978..7babb7de 100644 --- a/test/kernel/copy_file_test.cpp +++ b/test/kernel/copy_file_test.cpp @@ -31,12 +31,12 @@ void open(const std::string &dir_path) { } const std::string &original_dir_path() { - const static std::string path(test_utility::make_test_dir_path("CopyFileTest")); + const static std::string path(test_utility::make_test_path("/original")); return path; } const std::string ©_dir_path() { - const static std::string path(test_utility::make_test_dir_path("CopyFileTest_copy")); + const static std::string path(test_utility::make_test_path("/copy")); return path; } diff --git a/test/kernel/manager_multithread_test.cpp b/test/kernel/manager_multithread_test.cpp index 19072974..dcaca628 100644 --- a/test/kernel/manager_multithread_test.cpp +++ b/test/kernel/manager_multithread_test.cpp @@ -100,7 +100,7 @@ template void run_alloc_dealloc_separated_test(const list_type &allocation_size_list) { // Allocate manager - const auto dir(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + const auto dir(test_utility::make_test_path()); manager_type manager(metall::create_only, dir.c_str()); // Main loop @@ -132,7 +132,7 @@ template void run_alloc_dealloc_mixed_and_write_value_test(const list_type &allocation_size_list) { // Allocate manager - const auto dir(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + const auto dir(test_utility::make_test_path()); manager_type manager(metall::create_only, dir.c_str()); // Main loop @@ -276,7 +276,7 @@ TEST(ManagerMultithreadsTest, ConstructAndFind) { using allocation_element_type = std::array; constexpr std::size_t num_allocates = 1024; - const auto dir(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + const auto dir(test_utility::make_test_path()); manager_type manager(metall::create_only, dir.c_str()); std::vector keys; diff --git a/test/kernel/manager_test.cpp b/test/kernel/manager_test.cpp index 44d240a2..62385614 100644 --- a/test/kernel/manager_test.cpp +++ b/test/kernel/manager_test.cpp @@ -25,7 +25,7 @@ using object_size_mngr = metall::kernel::object_size_manager; const std::string &test_dir() { - const static std::string path(test_utility::make_test_dir_path("MultifileSegmentStorageTest")); + const static std::string path(test_utility::make_test_path()); return path; } diff --git a/test/kernel/multilayer_bitset_test.cpp b/test/kernel/multilayer_bitset_test.cpp index 6bcd1757..ae3bbb0b 100644 --- a/test/kernel/multilayer_bitset_test.cpp +++ b/test/kernel/multilayer_bitset_test.cpp @@ -60,22 +60,20 @@ TEST(MultilayerBitsetTest, NumInternalTrees) { TEST(MultilayerBitsetTest, FindAndSet) { for (uint64_t num_bits = 1; num_bits < (64ULL * 64 * 64 * 32); num_bits *= 64) { // Test up to 4 layers, - metall::kernel::multilayer_bitset> bitset; - auto allocator = typename metall::kernel::multilayer_bitset>::rebind_allocator_type(); - bitset.allocate(num_bits, allocator); + metall::kernel::multilayer_bitset bitset; + bitset.allocate(num_bits); for (uint64_t i = 0; i < num_bits; ++i) { ASSERT_EQ(bitset.find_and_set(num_bits), i); ASSERT_TRUE(bitset.get(num_bits, i)); } - bitset.free(num_bits, allocator); + bitset.free(num_bits); } } TEST(MultilayerBitsetTest, Reset) { for (uint64_t num_bits = 1; num_bits < (64ULL * 64 * 64 * 32); num_bits *= 64) { // Test up to 4 layers - metall::kernel::multilayer_bitset> bitset; - auto allocator = typename metall::kernel::multilayer_bitset>::rebind_allocator_type(); - bitset.allocate(num_bits, allocator); + metall::kernel::multilayer_bitset bitset; + bitset.allocate(num_bits); for (uint64_t i = 0; i < num_bits; ++i) { bitset.find_and_set(num_bits); } @@ -86,16 +84,15 @@ TEST(MultilayerBitsetTest, Reset) { ASSERT_EQ(bitset.find_and_set(num_bits), i); } - bitset.free(num_bits, allocator); + bitset.free(num_bits); } } void RandomSetHelper(const std::size_t num_bits) { SCOPED_TRACE("num_bits = " + std::to_string(num_bits)); - metall::kernel::multilayer_bitset> bitset; - auto allocator = typename metall::kernel::multilayer_bitset>::rebind_allocator_type(); - bitset.allocate(num_bits, allocator); + metall::kernel::multilayer_bitset bitset; + bitset.allocate(num_bits); std::vector reference(num_bits, false); @@ -133,7 +130,7 @@ void RandomSetHelper(const std::size_t num_bits) { } } - bitset.free(num_bits, allocator); + bitset.free(num_bits); } TEST(MultilayerBitsetTest, RandomSet) { @@ -168,9 +165,8 @@ TEST(MultilayerBitsetTest, RandomSet) { void RandomSetAndResetHelper(const std::size_t num_bits) { SCOPED_TRACE("num_bits = " + std::to_string(num_bits)); - metall::kernel::multilayer_bitset> bitset; - auto allocator = typename metall::kernel::multilayer_bitset>::rebind_allocator_type(); - bitset.allocate(num_bits, allocator); + metall::kernel::multilayer_bitset bitset; + bitset.allocate(num_bits); std::vector reference(num_bits, false); @@ -196,7 +192,7 @@ void RandomSetAndResetHelper(const std::size_t num_bits) { } } - bitset.free(num_bits, allocator); + bitset.free(num_bits); } TEST(MultilayerBitsetTest, RandomSetAndReset) { diff --git a/test/kernel/multimanager_test.cpp b/test/kernel/multimanager_test.cpp index 8f241f44..3651f240 100644 --- a/test/kernel/multimanager_test.cpp +++ b/test/kernel/multimanager_test.cpp @@ -29,10 +29,9 @@ TEST(MultiManagerTest, SingleThread) { using element_type = uint64_t; using vector_type = boost::interprocess::vector>; - const auto dir_path1(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name() - + std::to_string(1))); - const auto dir_path2(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name() - + std::to_string(2))); + test_utility::create_test_dir(); + const auto dir_path1(test_utility::make_test_path(std::to_string(1))); + const auto dir_path2(test_utility::make_test_path(std::to_string(2))); { manager_type manager1(metall::create_only, dir_path1.c_str(), k_chunk_size * 8); @@ -108,8 +107,7 @@ TEST(MultiManagerTest, MultiThread) { OMP_DIRECTIVE(parallel) { - const auto dir_path(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name() - + std::to_string(omp::get_thread_num()))); + 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(), k_chunk_size * 16); vector_type *vector = manager.construct("vector")(manager.get_allocator<>()); @@ -120,8 +118,7 @@ TEST(MultiManagerTest, MultiThread) { } for (int t = 0; t < get_num_threads(); ++t) { - const auto dir_path(test_utility::make_test_dir_path(::testing::UnitTest::GetInstance()->current_test_info()->name() - + std::to_string(t))); + const auto dir_path(test_utility::make_test_path("/" + std::to_string(t))); manager_type manager(metall::open_only, dir_path.c_str()); vector_type *vector; diff --git a/test/kernel/named_object_directory_test.cpp b/test/kernel/named_object_directory_test.cpp index 7d4b4252..55787229 100644 --- a/test/kernel/named_object_directory_test.cpp +++ b/test/kernel/named_object_directory_test.cpp @@ -6,107 +6,202 @@ #include "gtest/gtest.h" #include #include -#include #include "../test_utility.hpp" namespace { -using directory_type = metall::kernel::named_object_directory>; -using key_type = std::string; +using directory_type = metall::kernel::named_object_directory; -TEST(NambedObjectDirectoryTest, UniqueInsert) { - std::allocator allocator; - directory_type obj(allocator); +TEST(NambedObjectDirectoryTest, Insert) { + directory_type obj; ASSERT_TRUE(obj.insert("item1", 1, 1)); + ASSERT_TRUE(obj.insert("item2", 1, 1, "description2")); +} + +// Should insert uniquely +TEST(NambedObjectDirectoryTest, UniqueInsert) { + directory_type obj; + + obj.insert("item1", 1, 1); + ASSERT_FALSE(obj.insert("item1", 1, 1)); + + obj.insert("item2", 1, 1, "description2"); + ASSERT_FALSE(obj.insert("item2", 1, 1)); +} + +TEST(NambedObjectDirectoryTest, Count) { + directory_type obj; + + ASSERT_EQ(obj.count("item1"), 0); + obj.insert("item1", 1, 2); + ASSERT_EQ(obj.count("item1"), 1); + + ASSERT_EQ(obj.count("item2"), 0); + obj.insert("item2", 3, 4); + ASSERT_EQ(obj.count("item1"), 1); + ASSERT_EQ(obj.count("item2"), 1); +} - key_type name2{"item2"}; - ASSERT_TRUE(obj.insert(name2, 1, 1)); +TEST(NambedObjectDirectoryTest, GetValue) { + directory_type obj; - key_type name3{"item3"}; - ASSERT_TRUE(obj.insert(std::move(name3), 1, 1)); + typename directory_type::offset_type offset = 0; + typename directory_type::length_type length = 0; + + // Fails before insertions + ASSERT_FALSE(obj.get_offset("item1", &offset)); + ASSERT_FALSE(obj.get_length("item1", &length)); + obj.insert("item1", 1, 2); + + // Fails before insertions + ASSERT_FALSE(obj.get_offset("item2", &offset)); + ASSERT_FALSE(obj.get_length("item2", &length)); + obj.insert("item2", 3, 4); + + // Get values correctly + ASSERT_TRUE(obj.get_offset("item1", &offset)); + ASSERT_EQ(offset, 1); + ASSERT_TRUE(obj.get_length("item1", &length)); + ASSERT_EQ(length, 2); + + // Get values correctly + ASSERT_TRUE(obj.get_offset("item2", &offset)); + ASSERT_EQ(offset, 3); + ASSERT_TRUE(obj.get_length("item2", &length)); + ASSERT_EQ(length, 4); } -TEST(NambedObjectDirectoryTest, DuplicateInsert) { - std::allocator allocator; - directory_type obj(allocator); +TEST(NambedObjectDirectoryTest, Description) { + directory_type obj; + + directory_type::description_type buf; + // Operations should fails for non-existing name + ASSERT_FALSE(obj.get_description("item1", &buf)); + ASSERT_FALSE(obj.set_description("item1", buf)); + + // Check empty description obj.insert("item1", 1, 1); - ASSERT_FALSE(obj.insert("item1", 1, 1)); + ASSERT_TRUE(obj.get_description("item1", &buf)); + ASSERT_TRUE(buf.empty()); - key_type name2{"item2"}; - obj.insert(name2, 1, 1); - ASSERT_FALSE(obj.insert(name2, 1, 1)); + // Set an description + buf = "Description1"; + ASSERT_TRUE(obj.set_description("item1", buf)); - key_type name3_1{"item3"}; - obj.insert(std::move(name3_1), 1, 1); - key_type name3_2{"item3"}; - ASSERT_FALSE(obj.insert(std::move(name3_2), 1, 1)); + // Get an description + directory_type::description_type buf2; + ASSERT_TRUE(obj.get_description("item1", &buf2)); + ASSERT_EQ(buf, buf2); } -TEST(NambedObjectDirectoryTest, Find) { - std::allocator allocator; - directory_type obj(allocator); +TEST(NambedObjectDirectoryTest, Erase) { + directory_type obj; + ASSERT_EQ(obj.erase("item1"), 0); obj.insert("item1", 1, 2); + + ASSERT_EQ(obj.erase("item2"), 0); obj.insert("item2", 3, 4); - ASSERT_EQ(std::get<1>(obj.find("item1")->second), 1); - ASSERT_EQ(std::get<2>(obj.find("item1")->second), 2); - ASSERT_EQ(std::get<1>(obj.find("item2")->second), 3); - ASSERT_EQ(std::get<2>(obj.find("item2")->second), 4); - ASSERT_EQ(obj.find("item3"), obj.end()); + ASSERT_EQ(obj.erase("item1"), 1); + ASSERT_EQ(obj.count("item1"), 0); + ASSERT_EQ(obj.erase("item1"), 0); + + ASSERT_EQ(obj.erase("item2"), 1); + ASSERT_EQ(obj.count("item2"), 0); + ASSERT_EQ(obj.erase("item2"), 0); } -TEST(NambedObjectDirectoryTest, FindAndErase) { - std::allocator allocator; - directory_type obj(allocator); +TEST(NambedObjectDirectoryTest, KeyIterator) { + directory_type obj; + ASSERT_EQ(obj.keys_begin(), obj.keys_end()); obj.insert("item1", 1, 2); obj.insert("item2", 3, 4); - ASSERT_EQ(std::get<1>(obj.find("item1")->second), 1); - ASSERT_EQ(std::get<2>(obj.find("item1")->second), 2); - obj.erase(obj.find("item1")); - ASSERT_EQ(obj.find("item1"), obj.end()); + int count = 0; + bool found1 = false; + bool found2 = false; + for (auto itr = obj.keys_begin(); itr != obj.keys_end(); ++itr) { + ASSERT_TRUE(*itr == "item1" || *itr == "item2"); + found1 |= *itr == "item1"; + found2 |= *itr == "item2"; + ++count; + } + ASSERT_TRUE(found1); + ASSERT_TRUE(found2); + ASSERT_EQ(count, 2); + + obj.erase("item1"); + ASSERT_EQ(*(obj.keys_begin()), "item2"); - ASSERT_EQ(std::get<1>(obj.find("item2")->second), 3); - ASSERT_EQ(std::get<2>(obj.find("item2")->second), 4); - obj.erase(obj.find("item2")); - ASSERT_EQ(obj.find("item2"), obj.end()); + obj.erase("item2"); + ASSERT_EQ(obj.keys_begin(), obj.keys_end()); } TEST(NambedObjectDirectoryTest, Serialize) { - std::allocator allocator; - directory_type obj(allocator); + directory_type obj; obj.insert("item1", 1, 2); - obj.insert("item2", 3, 4); + obj.insert("item2", 3, 4, "description2"); + + test_utility::create_test_dir(); + const auto file(test_utility::make_test_path()); - ASSERT_TRUE(test_utility::create_test_dir()); - const auto file(test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); ASSERT_TRUE(obj.serialize(file.c_str())); } TEST(NambedObjectDirectoryTest, Deserialize) { - const auto file(test_utility::make_test_file_path(::testing::UnitTest::GetInstance()->current_test_info()->name())); + test_utility::create_test_dir(); + const auto file(test_utility::make_test_path()); { - std::allocator allocator; - directory_type obj(allocator); + directory_type obj; obj.insert("item1", 1, 2); - obj.insert("item2", 3, 4); + obj.insert("item2", 3, 4, "description2"); obj.serialize(file.c_str()); } { - std::allocator allocator; - directory_type obj(allocator); + directory_type obj; ASSERT_TRUE(obj.deserialize(file.c_str())); - ASSERT_EQ(std::get<1>(obj.find("item1")->second), 1); - ASSERT_EQ(std::get<2>(obj.find("item1")->second), 2); - ASSERT_EQ(std::get<1>(obj.find("item2")->second), 3); - ASSERT_EQ(std::get<2>(obj.find("item2")->second), 4); + + typename directory_type::offset_type offset = 0; + typename directory_type::length_type length = 0; + directory_type::description_type description; + + // Get values correctly + ASSERT_TRUE(obj.get_offset("item1", &offset)); + ASSERT_EQ(offset, 1); + ASSERT_TRUE(obj.get_length("item1", &length)); + ASSERT_EQ(length, 2); + ASSERT_TRUE(obj.get_description("item1", &description)); + ASSERT_TRUE(description.empty()); + + + // Get values correctly + ASSERT_TRUE(obj.get_offset("item2", &offset)); + ASSERT_EQ(offset, 3); + ASSERT_TRUE(obj.get_length("item2", &length)); + ASSERT_EQ(length, 4); + ASSERT_TRUE(obj.get_description("item2", &description)); + ASSERT_EQ(description, "description2"); + + // Key table is also restored + int count = 0; + bool found1 = false; + bool found2 = false; + for (auto itr = obj.keys_begin(); itr != obj.keys_end(); ++itr) { + ASSERT_TRUE(*itr == "item1" || *itr == "item2"); + found1 |= *itr == "item1"; + found2 |= *itr == "item2"; + ++count; + } + ASSERT_TRUE(found1); + ASSERT_TRUE(found2); + ASSERT_EQ(count, 2); } } diff --git a/test/kernel/snapshot_test.cpp b/test/kernel/snapshot_test.cpp index e2857b63..ce8ba694 100644 --- a/test/kernel/snapshot_test.cpp +++ b/test/kernel/snapshot_test.cpp @@ -7,54 +7,57 @@ #include "gtest/gtest.h" #include -#include -#include - -#include #include -#include #include "../test_utility.hpp" namespace { std::string original_dir_path() { - const std::string path(test_utility::make_test_dir_path("SnapshotTest")); + const std::string path(test_utility::make_test_path("original")); return path; } std::string snapshot_dir_path() { - const std::string path(test_utility::make_test_dir_path("SnapshotTest_snapshot")); + const std::string 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 manager(metall::create_only, original_dir_path().c_str()); + { + metall::manager manager(metall::create_only, original_dir_path().c_str()); - [[maybe_unused]] auto a = manager.construct("a")(1); - [[maybe_unused]] auto b = manager.construct("b")(2); + [[maybe_unused]] auto a = manager.construct("a")(1); + [[maybe_unused]] auto b = manager.construct("b")(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().c_str())); + ASSERT_TRUE(metall::manager::consistent(snapshot_dir_path().c_str())); - const auto original_uuid = metall::manager::get_uuid(original_dir_path().c_str()); - ASSERT_FALSE(original_uuid.empty()); - const auto snapshot_uuid = metall::manager::get_uuid(snapshot_dir_path().c_str()); - ASSERT_FALSE(snapshot_uuid.empty()); - ASSERT_NE(original_uuid, snapshot_uuid); -} + // UUID + const auto original_uuid = metall::manager::get_uuid(original_dir_path().c_str()); + ASSERT_FALSE(original_uuid.empty()); + const auto snapshot_uuid = metall::manager::get_uuid(snapshot_dir_path().c_str()); + 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())); + } -TEST(SnapshotTest, Open) { - metall::manager manager(metall::open_only, snapshot_dir_path().c_str()); + { + metall::manager manager(metall::open_read_only, snapshot_dir_path().c_str()); - auto a = manager.find("a").first; - ASSERT_EQ(*a, 1); + auto a = manager.find("a").first; + ASSERT_EQ(*a, 1); - auto b = manager.find("b").first; - ASSERT_EQ(*b, 2); + auto b = manager.find("b").first; + ASSERT_EQ(*b, 2); + } } } \ No newline at end of file diff --git a/test/test_utility.hpp b/test/test_utility.hpp index 6115b184..7aebc069 100644 --- a/test/test_utility.hpp +++ b/test/test_utility.hpp @@ -6,14 +6,17 @@ #ifndef METALL_TEST_UTILITY_HPP #define METALL_TEST_UTILITY_HPP +#include "gtest/gtest.h" + #include #include + #include namespace test_utility { const char *k_test_dir_env_name = "METALL_TEST_DIR"; -const char *k_default_test_dir = "/tmp"; +const char *k_default_test_dir = "/tmp/metall_test_dir"; namespace detail { inline std::string get_test_dir() { @@ -30,12 +33,11 @@ inline bool create_test_dir() { return true; } -inline std::string make_test_dir_path(const std::string &name) { - return detail::get_test_dir() + "/" + name; -} - -inline std::string make_test_file_path(const std::string &name) { - return detail::get_test_dir() + "/metall_test_" + name; +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; } }