From 43f998e49a7e84240fc462fddc2c7ebef3cb3df9 Mon Sep 17 00:00:00 2001 From: program-- Date: Thu, 14 Sep 2023 11:27:40 -0700 Subject: [PATCH 1/7] feat: mmap-backed allocator --- include/utilities/mmap_allocator.hpp | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 include/utilities/mmap_allocator.hpp diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp new file mode 100644 index 0000000000..9c47b49825 --- /dev/null +++ b/include/utilities/mmap_allocator.hpp @@ -0,0 +1,90 @@ +#ifndef NGEN_UTILITIES_MMAP_ALLOCATOR_HPP +#define NGEN_UTILITIES_MMAP_ALLOCATOR_HPP + +#include +#include +#include +#include + +namespace ngen { + +/** + * Reference-counted mmap-backed pointer. + * + * @tparam Tp element type + */ +template +struct mmap_pointer +{ + using element_type = Tp; + using pointer = Tp*; + using const_pointer = const Tp*; + using reference = Tp&; + using const_reference = const Tp&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr mmap_pointer(pointer ptr, size_type len) noexcept + : ptr_(make_mmap_pointer(ptr, len)){}; + + reference operator*() noexcept {return *ptr_;} + const_reference operator*() const noexcept {return *ptr_;} + pointer operator->() noexcept {return ptr_.get();} + const_pointer operator->() const noexcept {return ptr_.get();} + + private: + /** + * Get a lambda-defined `munmap` deleter that accepts a `pointer`. + * + * @param len Size of the pointer that'll be passed to `munmap` + * @return lambda function taking 1 `pointer` argument. + */ + static constexpr auto munmap_(size_type len) + { + return [=](pointer ptr){munmap(ptr, len);}; + } + + /** + * Create a mmap shared_ptr. + * + * @param ptr Pointer to mmap'd region + * @param len Size of mmap'd region + * @return std::shared_ptr + */ + static constexpr auto make_mmap_pointer(pointer ptr, size_type len) + { + return std::shared_ptr{ptr, munmap_(len)}; + } + + std::shared_ptr ptr_; +}; + +template +struct mmap_allocator +{ + using value_type = Tp; + using pointer = mmap_pointer; + using const_pointer = const mmap_pointer; + using size_type = typename pointer::size_type; + using difference_type = typename pointer::difference_type; + + using object_size = std::integral_constant; + + explicit mmap_allocator(const std::string& directory); + + mmap_allocator() noexcept + : mmap_allocator(BackendPolicy::default_directory){}; + + ~mmap_allocator() noexcept; + + pointer allocator(size_type n); + void deallocate(pointer p, size_type n); +}; + +struct tmpfs_backend { + static constexpr const char* default_directory = "/tmp/"; +}; + +} // namespace ngen + +#endif // NGEN_UTILITIES_MMAP_ALLOCATOR_HPP From 6115732b326d0ae77ec08be33fd61cabdea1f0fb Mon Sep 17 00:00:00 2001 From: program-- Date: Mon, 18 Sep 2023 13:59:51 -0700 Subject: [PATCH 2/7] feat: implement named requirements for iterators on mmap_pointer --- include/utilities/mmap_allocator.hpp | 166 ++++++++++------ include/utilities/mmap_pointer.hpp | 282 +++++++++++++++++++++++++++ test/utils/mmap_allocator_Test.cpp | 21 ++ 3 files changed, 405 insertions(+), 64 deletions(-) create mode 100644 include/utilities/mmap_pointer.hpp create mode 100644 test/utils/mmap_allocator_Test.cpp diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp index 9c47b49825..d245111d55 100644 --- a/include/utilities/mmap_allocator.hpp +++ b/include/utilities/mmap_allocator.hpp @@ -1,88 +1,126 @@ #ifndef NGEN_UTILITIES_MMAP_ALLOCATOR_HPP #define NGEN_UTILITIES_MMAP_ALLOCATOR_HPP -#include -#include -#include +#include +#include + +extern "C" { + #include +#include +#include + +} + +#include "mmap_pointer.hpp" namespace ngen { -/** - * Reference-counted mmap-backed pointer. - * - * @tparam Tp element type - */ -template -struct mmap_pointer +template +struct mmap_allocator { - using element_type = Tp; - using pointer = Tp*; - using const_pointer = const Tp*; - using reference = Tp&; - using const_reference = const Tp&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - constexpr mmap_pointer(pointer ptr, size_type len) noexcept - : ptr_(make_mmap_pointer(ptr, len)){}; - - reference operator*() noexcept {return *ptr_;} - const_reference operator*() const noexcept {return *ptr_;} - pointer operator->() noexcept {return ptr_.get();} - const_pointer operator->() const noexcept {return ptr_.get();} + using value_type = Tp; + using pointer = mmap_pointer; + using const_pointer = const mmap_pointer; + using size_type = typename pointer::size_type; + using difference_type = typename pointer::difference_type; - private: - /** - * Get a lambda-defined `munmap` deleter that accepts a `pointer`. - * - * @param len Size of the pointer that'll be passed to `munmap` - * @return lambda function taking 1 `pointer` argument. - */ - static constexpr auto munmap_(size_type len) + explicit mmap_allocator(std::string directory) noexcept + : dir_(std::move(directory)){}; + + mmap_allocator() noexcept + : mmap_allocator(PoolPolicy::default_directory()){}; + + pointer allocate(size_type n) { - return [=](pointer ptr){munmap(ptr, len);}; + const size_type mem_size = sizeof(value_type) * n; + std::pair finfo = PoolPolicy::open(dir_); + const int fd = finfo.first; + + // Truncate mmap file to allocated size + if (ftruncate(fd, mem_size) < 0) { + throw std::bad_alloc(); + } + + // Map file into virtual memory + void* ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + // Close file descriptor + PoolPolicy::close(fd); + + if (ptr == nullptr || ptr == MAP_FAILED) { + throw std::bad_alloc(); + } + + return pointer{ static_cast(ptr), std::move(finfo.second) }; } - /** - * Create a mmap shared_ptr. - * - * @param ptr Pointer to mmap'd region - * @param len Size of mmap'd region - * @return std::shared_ptr - */ - static constexpr auto make_mmap_pointer(pointer ptr, size_type len) + void deallocate(pointer p, size_type n) noexcept { - return std::shared_ptr{ptr, munmap_(len)}; + munmap(static_cast(p), n); } - std::shared_ptr ptr_; -}; - -template -struct mmap_allocator -{ - using value_type = Tp; - using pointer = mmap_pointer; - using const_pointer = const mmap_pointer; - using size_type = typename pointer::size_type; - using difference_type = typename pointer::difference_type; + template + friend bool operator==(const mmap_allocator& lhs, const mmap_allocator rhs) + { + if (std::is_same::value) { + return lhs.dir_ == rhs.dir_; + } - using object_size = std::integral_constant; + return false; + } - explicit mmap_allocator(const std::string& directory); + template + friend bool operator!=(const mmap_allocator& lhs, const mmap_allocator rhs) + { + if (std::is_same::value) { + return lhs.dir_ != rhs.dir_; + } - mmap_allocator() noexcept - : mmap_allocator(BackendPolicy::default_directory){}; - - ~mmap_allocator() noexcept; + return true; + } - pointer allocator(size_type n); - void deallocate(pointer p, size_type n); + private: + std::string dir_; }; -struct tmpfs_backend { - static constexpr const char* default_directory = "/tmp/"; +/** + * A basic file pool that creates files from a directory. + * Defaults to the system tempfs dir, which will be one of: + * - Environment variables TMPDIR, TMP, TEMP, or TEMPDIR + * - Or, `/tmp`. + */ +struct basic_pool { + static std::pair open(const std::string& directory) + { + std::string name = directory; + name += "/ngen_mmap_XXXXXX"; + mkstemp(&name[0]); + + const int fd = ::open(name.c_str(), O_CREAT | O_RDWR | O_TRUNC); + + ::unlink(name.c_str()); + return { fd, name }; + } + + static void close(int fd) + { + ::close(fd); + } + + static const char* default_directory() noexcept + { + // Per ISO/IEC 9945 (POSIX) + for (auto& var : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }) { + decltype(auto) env = std::getenv(var); + if (env != nullptr) { + return env; + } + } + + // Default + return "/tmp"; + } }; } // namespace ngen diff --git a/include/utilities/mmap_pointer.hpp b/include/utilities/mmap_pointer.hpp new file mode 100644 index 0000000000..8431872a05 --- /dev/null +++ b/include/utilities/mmap_pointer.hpp @@ -0,0 +1,282 @@ +#ifndef NGEN_UTILITIES_MMAP_POINTER_HPP +#define NGEN_UTILITIES_MMAP_POINTER_HPP + +#include +#include + +namespace ngen { + +/** + * Fancy pointer satisfying the following named requirements and their requirements: + * - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer) + * - [LegacyRandomAccessIterator](https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator) + * + * @tparam Tp type this pointer dereferences to + */ +template +struct mmap_pointer +{ + // type aliases are required per LegacyIterator. + + using value_type = Tp; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator_category = std::random_access_iterator_tag; + + explicit mmap_pointer(pointer ptr, std::string path) + : path_(std::move(path)) + , ptr_(ptr){}; + + // @requirement DefaultConstructible + explicit mmap_pointer() noexcept + : path_() + , ptr_(nullptr){}; + + // @requirement NullablePointer + explicit mmap_pointer(std::nullptr_t) noexcept + : mmap_pointer(){}; + + // @requirement NullablePointer + mmap_pointer& operator=(std::nullptr_t) noexcept + { + ptr_ = nullptr; + return *this; + } + + // @requirement CopyConstructible + mmap_pointer(const mmap_pointer&) noexcept = default; + + // @requirement CopyAssignable + mmap_pointer& operator=(const mmap_pointer&) noexcept = default; + + // @requirement MoveConstructible + mmap_pointer(mmap_pointer&&) noexcept = default; + + // @requirement MoveConstructible + mmap_pointer& operator=(mmap_pointer&&) noexcept = default; + + // @requirement Destructible + ~mmap_pointer() noexcept = default; + + // @requirement Swappable + void swap(mmap_pointer& other); + + operator bool() noexcept { + return ptr_ == nullptr; + } + + operator void*() noexcept { + return static_cast(ptr_); + } + + // @requirement LegacyRandomAccessIterator + reference operator[](difference_type n) noexcept + { + return ptr_[n]; + } + + // @requirement LegacyRandomAccessIterator + const_reference operator[](difference_type n) const noexcept + { + return ptr_[n]; + } + + // @requirement LegacyInputIterator + reference operator*() noexcept + { + return *ptr_; + } + + // @requirement LegacyInputIterator + const_reference operator*() const noexcept + { + return *ptr_; + } + + // @requirement LegacyInputIterator + pointer operator->() noexcept + { + return ptr_; + } + + // @requirement LegacyInputIterator + const_pointer operator->() const noexcept + { + return ptr_; + } + + // @requirement LegacyIterator + mmap_pointer& operator++() noexcept + { + ++ptr_; + return *this; + } + + // @requirement LegacyInputIterator + mmap_pointer& operator++(int) noexcept + { + mmap_pointer tmp = *this; + ++(*this); + return tmp; + } + + // @requirmeent LegacyBidirectionalIterator + mmap_pointer& operator--() noexcept + { + --ptr_; + return *this; + } + + // @requirmeent LegacyBidirectionalIterator + mmap_pointer& operator--(int) noexcept + { + mmap_pointer tmp = *this; + --(*this); + return tmp; + } + + // @requirement LegacyRandomAccessIterator + mmap_pointer& operator+=(difference_type n) noexcept + { + ptr_ += n; + return *this; + } + + // @requirement LegacyRandomAccessIterator + mmap_pointer operator+(difference_type n) const noexcept + { + mmap_pointer tmp = *this; + tmp.ptr_ += n; + return tmp; + } + + // @requirement LegacyRandomAccessIterator + mmap_pointer operator+(size_type n) const noexcept + { + mmap_pointer tmp = *this; + tmp.ptr_ += n; + return tmp; + } + + // @requirement LegacyRandomAccessIterator + friend mmap_pointer operator+(difference_type n, const mmap_pointer& a) noexcept + { + mmap_pointer tmp = a; + tmp.ptr_ += n; + return tmp; + } + + // @requirement LegacyRandomAccessIterator + mmap_pointer& operator-=(difference_type n) noexcept + { + ptr_ -= n; + return *this; + } + + // @requirement LegacyRandomAccessIterator + mmap_pointer operator-(difference_type n) const noexcept + { + mmap_pointer tmp = *this; + tmp.ptr_ -= n; + return tmp; + } + + // @requirement LegacyRandomAccessIterator + friend mmap_pointer operator-(difference_type n, const mmap_pointer& a) noexcept + { + mmap_pointer tmp = a; + tmp.ptr_ -= n; + return tmp; + } + + difference_type operator-(const mmap_pointer& rhs) const noexcept + { + return ptr_ - rhs.ptr_; + } + + // @requirement NullablePointer + bool operator==(std::nullptr_t) + { + return ptr_ == nullptr; + } + + // @requirement NullablePointer + friend bool operator==(std::nullptr_t, const mmap_pointer& rhs) + { + return rhs == nullptr; + } + + // @requirement NullablePointer + bool operator!=(std::nullptr_t) + { + return ptr_ != nullptr; + } + + // @requirement NullablePointer + friend bool operator!=(std::nullptr_t, const mmap_pointer& rhs) + { + return rhs != nullptr; + } + + // @requirement EqualityComparable + template + friend bool operator==(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + if (std::is_same::value) { + return lhs.ptr_ == rhs.ptr_; + } + + return false; + } + + // @requirement LegacyInputIterator + template + friend bool operator!=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + return !(lhs == rhs); + } + + // @requirement LegacyRandomAccessIterator + friend bool operator<(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + return lhs.ptr_ < rhs.ptr_; + } + + // @requirement LegacyRandomAccessIterator + friend bool operator>(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + return lhs.ptr_ > rhs.ptr_; + } + + // @requirement LegacyRandomAccessIterator + friend bool operator<=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + return lhs.ptr_ <= rhs.ptr_; + } + + // @requirement LegacyRandomAccessIterator + friend bool operator>=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept + { + return lhs.ptr_ >= rhs.ptr_; + } + + // ------------------------------------------------------------------------ + + const std::string& path() const noexcept + { + return path_; + } + + private: + std::string path_; + pointer ptr_; +}; + + +} // namespace ngen + +#endif // NGEN_UTILITIES_MMAP_POINTER_HPP diff --git a/test/utils/mmap_allocator_Test.cpp b/test/utils/mmap_allocator_Test.cpp new file mode 100644 index 0000000000..08c2658bb3 --- /dev/null +++ b/test/utils/mmap_allocator_Test.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +TEST(mmap_allocator_Test, allocation) { + using alloc = ngen::mmap_allocator; + + alloc mmapper{"/home/jsinghm/Documents/ngen/"}; + std::vector mmap_vector(5, mmapper); + + EXPECT_EQ(mmap_vector.capacity(), 5); + + std::iota(mmap_vector.begin(), mmap_vector.end(), 0); + + for (size_t i = 0; i < mmap_vector.size(); i++) { + EXPECT_EQ(i, mmap_vector[i]); + } + + // ASSERT_NO_THROW(mmap_vector.reserve(10)); + // EXPECT_EQ(mmap_vector.capacity(), 10); +} From 789cc234813baac238a3c16d41f3dc98d8886e80 Mon Sep 17 00:00:00 2001 From: program-- Date: Mon, 18 Sep 2023 14:37:22 -0700 Subject: [PATCH 3/7] refactor: rm mmap_pointer; use raw pointer and unlink file at allocation --- include/utilities/mmap_allocator.hpp | 23 +-- include/utilities/mmap_pointer.hpp | 282 --------------------------- test/CMakeLists.txt | 8 + test/utils/mmap_allocator_Test.cpp | 15 +- 4 files changed, 25 insertions(+), 303 deletions(-) delete mode 100644 include/utilities/mmap_pointer.hpp diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp index d245111d55..ef1a19847b 100644 --- a/include/utilities/mmap_allocator.hpp +++ b/include/utilities/mmap_allocator.hpp @@ -12,18 +12,16 @@ extern "C" { } -#include "mmap_pointer.hpp" - namespace ngen { template struct mmap_allocator { using value_type = Tp; - using pointer = mmap_pointer; - using const_pointer = const mmap_pointer; - using size_type = typename pointer::size_type; - using difference_type = typename pointer::difference_type; + using pointer = value_type*; + using const_pointer = const value_type*; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; explicit mmap_allocator(std::string directory) noexcept : dir_(std::move(directory)){}; @@ -34,8 +32,7 @@ struct mmap_allocator pointer allocate(size_type n) { const size_type mem_size = sizeof(value_type) * n; - std::pair finfo = PoolPolicy::open(dir_); - const int fd = finfo.first; + const int fd = PoolPolicy::open(dir_); // Truncate mmap file to allocated size if (ftruncate(fd, mem_size) < 0) { @@ -43,7 +40,7 @@ struct mmap_allocator } // Map file into virtual memory - void* ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + void* ptr = mmap(nullptr, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Close file descriptor PoolPolicy::close(fd); @@ -52,7 +49,7 @@ struct mmap_allocator throw std::bad_alloc(); } - return pointer{ static_cast(ptr), std::move(finfo.second) }; + return pointer{ static_cast(ptr) }; } void deallocate(pointer p, size_type n) noexcept @@ -91,16 +88,16 @@ struct mmap_allocator * - Or, `/tmp`. */ struct basic_pool { - static std::pair open(const std::string& directory) + static int open(const std::string& directory) { std::string name = directory; name += "/ngen_mmap_XXXXXX"; mkstemp(&name[0]); const int fd = ::open(name.c_str(), O_CREAT | O_RDWR | O_TRUNC); - + ::unlink(name.c_str()); - return { fd, name }; + return fd; } static void close(int fd) diff --git a/include/utilities/mmap_pointer.hpp b/include/utilities/mmap_pointer.hpp deleted file mode 100644 index 8431872a05..0000000000 --- a/include/utilities/mmap_pointer.hpp +++ /dev/null @@ -1,282 +0,0 @@ -#ifndef NGEN_UTILITIES_MMAP_POINTER_HPP -#define NGEN_UTILITIES_MMAP_POINTER_HPP - -#include -#include - -namespace ngen { - -/** - * Fancy pointer satisfying the following named requirements and their requirements: - * - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer) - * - [LegacyRandomAccessIterator](https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator) - * - * @tparam Tp type this pointer dereferences to - */ -template -struct mmap_pointer -{ - // type aliases are required per LegacyIterator. - - using value_type = Tp; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - using iterator_category = std::random_access_iterator_tag; - - explicit mmap_pointer(pointer ptr, std::string path) - : path_(std::move(path)) - , ptr_(ptr){}; - - // @requirement DefaultConstructible - explicit mmap_pointer() noexcept - : path_() - , ptr_(nullptr){}; - - // @requirement NullablePointer - explicit mmap_pointer(std::nullptr_t) noexcept - : mmap_pointer(){}; - - // @requirement NullablePointer - mmap_pointer& operator=(std::nullptr_t) noexcept - { - ptr_ = nullptr; - return *this; - } - - // @requirement CopyConstructible - mmap_pointer(const mmap_pointer&) noexcept = default; - - // @requirement CopyAssignable - mmap_pointer& operator=(const mmap_pointer&) noexcept = default; - - // @requirement MoveConstructible - mmap_pointer(mmap_pointer&&) noexcept = default; - - // @requirement MoveConstructible - mmap_pointer& operator=(mmap_pointer&&) noexcept = default; - - // @requirement Destructible - ~mmap_pointer() noexcept = default; - - // @requirement Swappable - void swap(mmap_pointer& other); - - operator bool() noexcept { - return ptr_ == nullptr; - } - - operator void*() noexcept { - return static_cast(ptr_); - } - - // @requirement LegacyRandomAccessIterator - reference operator[](difference_type n) noexcept - { - return ptr_[n]; - } - - // @requirement LegacyRandomAccessIterator - const_reference operator[](difference_type n) const noexcept - { - return ptr_[n]; - } - - // @requirement LegacyInputIterator - reference operator*() noexcept - { - return *ptr_; - } - - // @requirement LegacyInputIterator - const_reference operator*() const noexcept - { - return *ptr_; - } - - // @requirement LegacyInputIterator - pointer operator->() noexcept - { - return ptr_; - } - - // @requirement LegacyInputIterator - const_pointer operator->() const noexcept - { - return ptr_; - } - - // @requirement LegacyIterator - mmap_pointer& operator++() noexcept - { - ++ptr_; - return *this; - } - - // @requirement LegacyInputIterator - mmap_pointer& operator++(int) noexcept - { - mmap_pointer tmp = *this; - ++(*this); - return tmp; - } - - // @requirmeent LegacyBidirectionalIterator - mmap_pointer& operator--() noexcept - { - --ptr_; - return *this; - } - - // @requirmeent LegacyBidirectionalIterator - mmap_pointer& operator--(int) noexcept - { - mmap_pointer tmp = *this; - --(*this); - return tmp; - } - - // @requirement LegacyRandomAccessIterator - mmap_pointer& operator+=(difference_type n) noexcept - { - ptr_ += n; - return *this; - } - - // @requirement LegacyRandomAccessIterator - mmap_pointer operator+(difference_type n) const noexcept - { - mmap_pointer tmp = *this; - tmp.ptr_ += n; - return tmp; - } - - // @requirement LegacyRandomAccessIterator - mmap_pointer operator+(size_type n) const noexcept - { - mmap_pointer tmp = *this; - tmp.ptr_ += n; - return tmp; - } - - // @requirement LegacyRandomAccessIterator - friend mmap_pointer operator+(difference_type n, const mmap_pointer& a) noexcept - { - mmap_pointer tmp = a; - tmp.ptr_ += n; - return tmp; - } - - // @requirement LegacyRandomAccessIterator - mmap_pointer& operator-=(difference_type n) noexcept - { - ptr_ -= n; - return *this; - } - - // @requirement LegacyRandomAccessIterator - mmap_pointer operator-(difference_type n) const noexcept - { - mmap_pointer tmp = *this; - tmp.ptr_ -= n; - return tmp; - } - - // @requirement LegacyRandomAccessIterator - friend mmap_pointer operator-(difference_type n, const mmap_pointer& a) noexcept - { - mmap_pointer tmp = a; - tmp.ptr_ -= n; - return tmp; - } - - difference_type operator-(const mmap_pointer& rhs) const noexcept - { - return ptr_ - rhs.ptr_; - } - - // @requirement NullablePointer - bool operator==(std::nullptr_t) - { - return ptr_ == nullptr; - } - - // @requirement NullablePointer - friend bool operator==(std::nullptr_t, const mmap_pointer& rhs) - { - return rhs == nullptr; - } - - // @requirement NullablePointer - bool operator!=(std::nullptr_t) - { - return ptr_ != nullptr; - } - - // @requirement NullablePointer - friend bool operator!=(std::nullptr_t, const mmap_pointer& rhs) - { - return rhs != nullptr; - } - - // @requirement EqualityComparable - template - friend bool operator==(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - if (std::is_same::value) { - return lhs.ptr_ == rhs.ptr_; - } - - return false; - } - - // @requirement LegacyInputIterator - template - friend bool operator!=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - return !(lhs == rhs); - } - - // @requirement LegacyRandomAccessIterator - friend bool operator<(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - return lhs.ptr_ < rhs.ptr_; - } - - // @requirement LegacyRandomAccessIterator - friend bool operator>(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - return lhs.ptr_ > rhs.ptr_; - } - - // @requirement LegacyRandomAccessIterator - friend bool operator<=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - return lhs.ptr_ <= rhs.ptr_; - } - - // @requirement LegacyRandomAccessIterator - friend bool operator>=(const mmap_pointer& lhs, const mmap_pointer& rhs) noexcept - { - return lhs.ptr_ >= rhs.ptr_; - } - - // ------------------------------------------------------------------------ - - const std::string& path() const noexcept - { - return path_; - } - - private: - std::string path_; - pointer ptr_; -}; - - -} // namespace ngen - -#endif // NGEN_UTILITIES_MMAP_POINTER_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b050239c1c..42c38778db 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -140,6 +140,14 @@ ngen_add_test( NGen::logging ) +########################## Allocator Tests +ngen_add_test( + test_allocator + OBJECTS + utils/mmap_allocator_Test.cpp +) +# TODO: Workaround for now -- only needs header +target_include_directories(test_allocator PRIVATE ${NGEN_INC_DIR}/utilities) ########################## Nexus Tests ngen_add_test( diff --git a/test/utils/mmap_allocator_Test.cpp b/test/utils/mmap_allocator_Test.cpp index 08c2658bb3..d9f1fe7a57 100644 --- a/test/utils/mmap_allocator_Test.cpp +++ b/test/utils/mmap_allocator_Test.cpp @@ -2,20 +2,19 @@ #include #include -TEST(mmap_allocator_Test, allocation) { +TEST(mmap_allocator_Test, basicPool) { using alloc = ngen::mmap_allocator; - - alloc mmapper{"/home/jsinghm/Documents/ngen/"}; - std::vector mmap_vector(5, mmapper); + std::vector mmap_vector(5); EXPECT_EQ(mmap_vector.capacity(), 5); std::iota(mmap_vector.begin(), mmap_vector.end(), 0); - for (size_t i = 0; i < mmap_vector.size(); i++) { - EXPECT_EQ(i, mmap_vector[i]); + size_t i = 0; + for (auto& v : mmap_vector) { + EXPECT_NEAR(i++, v, 1e-6); } - // ASSERT_NO_THROW(mmap_vector.reserve(10)); - // EXPECT_EQ(mmap_vector.capacity(), 10); + ASSERT_NO_THROW(mmap_vector.reserve(15)); + EXPECT_EQ(mmap_vector.capacity(), 15); } From 3ea20428ca232c7597e55ef0ecd63a655c352df9 Mon Sep 17 00:00:00 2001 From: program-- Date: Mon, 18 Sep 2023 14:45:02 -0700 Subject: [PATCH 4/7] fix: static_cast instead of uniform init --- include/utilities/mmap_allocator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp index ef1a19847b..3f5403685e 100644 --- a/include/utilities/mmap_allocator.hpp +++ b/include/utilities/mmap_allocator.hpp @@ -49,7 +49,7 @@ struct mmap_allocator throw std::bad_alloc(); } - return pointer{ static_cast(ptr) }; + return static_cast(ptr); } void deallocate(pointer p, size_type n) noexcept From 937bf1cf335c779f7e4788ee6552e47100698632 Mon Sep 17 00:00:00 2001 From: program-- Date: Mon, 18 Sep 2023 14:52:43 -0700 Subject: [PATCH 5/7] fix: incorrect munmap size; docs: add code docs --- include/utilities/mmap_allocator.hpp | 55 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp index 3f5403685e..3e795782bd 100644 --- a/include/utilities/mmap_allocator.hpp +++ b/include/utilities/mmap_allocator.hpp @@ -2,7 +2,6 @@ #define NGEN_UTILITIES_MMAP_ALLOCATOR_HPP #include -#include extern "C" { @@ -14,6 +13,12 @@ extern "C" { namespace ngen { +/** + * @brief Memory mapped allocator. + * + * @tparam Tp Type that is allocated + * @tparam PoolPolicy Backing pool policy; see `ngen::basic_pool` + */ template struct mmap_allocator { @@ -23,12 +28,30 @@ struct mmap_allocator using size_type = std::size_t; using difference_type = std::ptrdiff_t; + /** + * @brief Create a new mmap allocator with the given directory + * as the storage location. + * + * @param directory Path to storage location + */ explicit mmap_allocator(std::string directory) noexcept : dir_(std::move(directory)){}; + /** + * @brief Default construct a mmap allocator. + * + * @note Uses PoolPolicy::default_directory() as the storage location. + * + */ mmap_allocator() noexcept : mmap_allocator(PoolPolicy::default_directory()){}; + /** + * @brief Allocate `n` objects of type `Tp`, aka `sizeof(Tp) * n`. + * + * @param n Number of elements to allocate + * @return pointer + */ pointer allocate(size_type n) { const size_type mem_size = sizeof(value_type) * n; @@ -52,9 +75,15 @@ struct mmap_allocator return static_cast(ptr); } + /** + * @brief Deallocate `n` elements starting at address `p`. + * + * @param p Pointer to beginning address + * @param n Number of elements to deallocate + */ void deallocate(pointer p, size_type n) noexcept { - munmap(static_cast(p), n); + munmap(static_cast(p), sizeof(value_type) * n); } template @@ -88,6 +117,12 @@ struct mmap_allocator * - Or, `/tmp`. */ struct basic_pool { + /** + * @brief Open a new pool file in `directory` + * + * @param directory Storage location + * @return int File descriptor + */ static int open(const std::string& directory) { std::string name = directory; @@ -100,11 +135,27 @@ struct basic_pool { return fd; } + /** + * @brief Close the given file descriptor + * + * @param fd File descriptor + */ static void close(int fd) { ::close(fd); } + /** + * @brief Get the default directory for this pool. + * + * @details + * Defaults to "/tmp", or the first value set + * in the following environment variables: + * + * TMPDIR, TMP, TEMP, or TEMPDIR. + * + * @return const char* Absolute file path + */ static const char* default_directory() noexcept { // Per ISO/IEC 9945 (POSIX) From 200b966fac9370e504a40c021602d27fa890d9a0 Mon Sep 17 00:00:00 2001 From: program-- Date: Mon, 18 Sep 2023 15:03:01 -0700 Subject: [PATCH 6/7] tests: add mmap_allocator test to aggregate targets --- test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 42c38778db..5644e628af 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -369,6 +369,7 @@ ngen_add_test( utils/mdframe_netcdf_Test.cpp utils/mdframe_csv_Test.cpp utils/logging_Test.cpp + utils/mmap_allocator_Test.cpp LIBRARIES gmock NGen::core @@ -406,6 +407,7 @@ ngen_add_test( utils/mdframe_netcdf_Test.cpp utils/mdframe_csv_Test.cpp utils/logging_Test.cpp + utils/mmap_allocator_Test.cpp LIBRARIES NGen::core gmock From a2cbcd018cc015a6a3726bcfd2784621811747d4 Mon Sep 17 00:00:00 2001 From: program-- Date: Tue, 19 Sep 2023 06:38:05 -0700 Subject: [PATCH 7/7] fix: specify mode for ::open call --- include/utilities/mmap_allocator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utilities/mmap_allocator.hpp b/include/utilities/mmap_allocator.hpp index 3e795782bd..2a7232186f 100644 --- a/include/utilities/mmap_allocator.hpp +++ b/include/utilities/mmap_allocator.hpp @@ -129,7 +129,7 @@ struct basic_pool { name += "/ngen_mmap_XXXXXX"; mkstemp(&name[0]); - const int fd = ::open(name.c_str(), O_CREAT | O_RDWR | O_TRUNC); + const int fd = ::open(name.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); ::unlink(name.c_str()); return fd;