diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp index e072b3c6c..5a3178505 100644 --- a/include/osmium/util/memory_mapping.hpp +++ b/include/osmium/util/memory_mapping.hpp @@ -38,6 +38,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include + #ifndef _WIN32 # include #else @@ -80,12 +82,12 @@ namespace osmium { /// The size of the mapping size_t m_size; - /// File handle we got the mapping from - int m_fd; - /// Offset into the file off_t m_offset; + /// File handle we got the mapping from + int m_fd; + /// Is the memory writable? bool m_writable; @@ -116,6 +118,19 @@ namespace osmium { void* osmium::util::MemoryMapping::map_view_of_file() const noexcept; #endif + int resize_fd(int fd) { + // Anonymous mapping doesn't need resizing. + if (fd == -1) { + return -1; + } + + // Make sure the file backing this mapping is large enough. + if (osmium::util::file_size(fd) < m_size + m_offset) { + osmium::util::resize_file(fd, m_size + m_offset); + } + return fd; + } + public: /** @@ -196,6 +211,15 @@ namespace osmium { return m_size; } + /** + * The file descriptor this mapping was created from. + * + * @returns file descriptor, -1 for anonymous mappings + */ + int fd() const noexcept { + return m_fd; + } + /** * Was this mapping created as a writable mapping? */ @@ -350,6 +374,15 @@ namespace osmium { return m_size; } + /** + * The file descriptor this mapping was created from. + * + * @returns file descriptor, -1 for anonymous mappings + */ + int fd() const noexcept { + return m_mapping.fd(); + } + /** * Was this mapping created as a writable mapping? */ @@ -429,11 +462,11 @@ inline int osmium::util::MemoryMapping::get_flags() const noexcept { } inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, int fd, off_t offset) : - m_size(size), - m_fd(fd), + m_size(osmium::util::round_to_pagesize(size)), m_offset(offset), + m_fd(resize_fd(fd)), m_writable(writable), - m_addr(::mmap(nullptr, size, get_protection(), get_flags(), m_fd, m_offset)) { + m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) { assert(writable || fd != -1); if (!is_valid()) { throw std::system_error(errno, std::system_category(), "mmap failed"); @@ -442,8 +475,8 @@ inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, in inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) : m_size(other.m_size), - m_fd(other.m_fd), m_offset(other.m_offset), + m_fd(other.m_fd), m_writable(other.m_writable), m_addr(other.m_addr) { other.make_invalid(); @@ -452,8 +485,8 @@ inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) : inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmium::util::MemoryMapping&& other) { unmap(); m_size = other.m_size; - m_fd = other.m_fd; m_offset = other.m_offset; + m_fd = other.m_fd; m_writable = other.m_writable; m_addr = other.m_addr; other.make_invalid(); @@ -470,20 +503,26 @@ inline void osmium::util::MemoryMapping::unmap() { } inline void osmium::util::MemoryMapping::resize(size_t new_size) { + if (m_fd == -1) { // anonymous mapping #ifdef __linux__ - m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE); - if (!is_valid()) { - throw std::system_error(errno, std::system_category(), "mremap failed"); - } + size_t old_size = m_size; + m_size = osmium::util::round_to_pagesize(new_size); + m_addr = ::mremap(m_addr, old_size, m_size, MREMAP_MAYMOVE); + if (!is_valid()) { + throw std::system_error(errno, std::system_category(), "mremap failed"); + } #else - assert(m_fd != -1); - unmap(); - m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset); - if (!is_valid()) { - throw std::system_error(errno, std::system_category(), "mmap failed"); - } + assert(false && "can't resize anonymous mappings on non-linux systems"); #endif - m_size = new_size; + } else { // file-based mapping + unmap(); + m_size = osmium::util::round_to_pagesize(new_size); + resize_fd(m_fd); + m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset); + if (!is_valid()) { + throw std::system_error(errno, std::system_category(), "mmap (remap) failed"); + } + } } #else @@ -550,9 +589,9 @@ inline void osmium::util::MemoryMapping::make_invalid() noexcept { } inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, int fd, off_t offset) : - m_size(size), - m_fd(fd), + m_size(osmium::util::round_to_pagesize(size)), m_offset(offset), + m_fd(resize_fd(fd)), m_writable(writable), m_handle(create_file_mapping()), m_addr(nullptr) { @@ -569,8 +608,8 @@ inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, in inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) : m_size(other.m_size), - m_fd(other.m_fd), m_offset(other.m_offset), + m_fd(other.m_fd), m_writable(other.m_writable), m_handle(std::move(other.m_handle)), m_addr(other.m_addr) { @@ -581,8 +620,8 @@ inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) : inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmium::util::MemoryMapping&& other) { unmap(); m_size = other.m_size; - m_fd = other.m_fd; m_offset = other.m_offset; + m_fd = other.m_fd; m_writable = other.m_writable; m_handle = std::move(other.m_handle); m_addr = other.m_addr; @@ -610,7 +649,8 @@ inline void osmium::util::MemoryMapping::unmap() { inline void osmium::util::MemoryMapping::resize(size_t new_size) { unmap(); - m_size = new_size; + m_size = osmium::util::round_to_pagesize(new_size); + resize_fd(m_fd); m_handle = create_file_mapping(); if (!m_handle) { diff --git a/test/t/util/test_memory_mapping.cpp b/test/t/util/test_memory_mapping.cpp index 2d8fed254..505c643b2 100644 --- a/test/t/util/test_memory_mapping.cpp +++ b/test/t/util/test_memory_mapping.cpp @@ -20,9 +20,11 @@ static const size_t huge = std::numeric_limits::max(); TEST_CASE("anonymous mapping") { SECTION("simple memory mapping should work") { - osmium::util::MemoryMapping mapping(1024); + osmium::util::MemoryMapping mapping(1000); REQUIRE(mapping.get_addr() != nullptr); + REQUIRE(mapping.size() >= 1000); + volatile int* addr = mapping.get_addr(); REQUIRE(mapping.writable()); @@ -36,13 +38,24 @@ TEST_CASE("anonymous mapping") { mapping.unmap(); // second unmap is okay } + SECTION("memory mapping of zero length should work") { + osmium::util::MemoryMapping mapping(0); + REQUIRE(mapping.get_addr() != nullptr); + + REQUIRE(mapping.size() == osmium::util::get_pagesize()); + + REQUIRE(!!mapping); + mapping.unmap(); + REQUIRE(!mapping); + } + SECTION("memory mapping a huge area should fail") { REQUIRE_THROWS_AS(osmium::util::MemoryMapping mapping(huge), std::system_error); } SECTION("moving a memory mapping should work") { - osmium::util::MemoryMapping mapping1(1024); + osmium::util::MemoryMapping mapping1(1000); int* addr1 = mapping1.get_addr(); *addr1 = 42; @@ -60,8 +73,8 @@ TEST_CASE("anonymous mapping") { } SECTION("move assignment should work") { - osmium::util::MemoryMapping mapping1(1024); - osmium::util::MemoryMapping mapping2(1024); + osmium::util::MemoryMapping mapping1(1000); + osmium::util::MemoryMapping mapping2(1000); REQUIRE(!!mapping1); REQUIRE(!!mapping2); @@ -82,26 +95,32 @@ TEST_CASE("anonymous mapping") { #ifdef __linux__ SECTION("remapping to larger size should work") { - osmium::util::MemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::MemoryMapping mapping(1000); + REQUIRE(mapping.size() >= 1000); + + size_t size1 = mapping.size(); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(2048); + mapping.resize(8000); + REQUIRE(mapping.size() > size1); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); } SECTION("remapping to smaller size should work") { - osmium::util::MemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::MemoryMapping mapping(8000); + REQUIRE(mapping.size() >= 1000); + + size_t size1 = mapping.size(); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(512); + mapping.resize(500); + REQUIRE(mapping.size() < size1); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); @@ -124,19 +143,21 @@ TEST_CASE("file-based mapping") { REQUIRE(mapping.writable()); REQUIRE(!!mapping); - REQUIRE(mapping.size() == 100); + REQUIRE(mapping.size() >= 100); *mapping.get_addr() = 1234; mapping.unmap(); } + REQUIRE(osmium::util::file_size(fd) == osmium::util::get_pagesize()); + { osmium::util::MemoryMapping mapping(100, false, fd); REQUIRE(!mapping.writable()); REQUIRE(!!mapping); - REQUIRE(mapping.size() == 100); + REQUIRE(mapping.size() >= 100); REQUIRE(*mapping.get_addr() == 1234); mapping.unmap(); @@ -151,16 +172,16 @@ TEST_CASE("file-based mapping") { const int fd = mkstemp(filename); REQUIRE(fd > 0); - REQUIRE(0 == ::ftruncate(fd, 200)); - osmium::util::MemoryMapping mapping(100, true, fd); - REQUIRE(mapping.size() == 100); + REQUIRE(mapping.size() >= 100); + size_t size1 = mapping.size(); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(200); - REQUIRE(mapping.size() == 200); + mapping.resize(8000); + REQUIRE(mapping.size() >= 8000); + REQUIRE(mapping.size() > size1); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); @@ -176,17 +197,17 @@ TEST_CASE("file-based mapping") { const int fd = mkstemp(filename); REQUIRE(fd > 0); - REQUIRE(0 == ::ftruncate(fd, 100)); - { - osmium::util::MemoryMapping mapping(100, true, fd); - REQUIRE(mapping.size() == 100); + osmium::util::MemoryMapping mapping(8000, true, fd); + REQUIRE(mapping.size() >= 8000); + size_t size1 = mapping.size(); int* addr1 = mapping.get_addr(); *addr1 = 42; mapping.resize(50); - REQUIRE(mapping.size() == 50); + REQUIRE(mapping.size() >= 50); + REQUIRE(mapping.size() < size1); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); @@ -200,7 +221,7 @@ TEST_CASE("file-based mapping") { TEST_CASE("typed anonymous mapping") { SECTION("simple memory mapping should work") { - osmium::util::TypedMemoryMapping mapping(1024); + osmium::util::TypedMemoryMapping mapping(1000); volatile uint32_t* addr = mapping.get_addr(); REQUIRE(mapping.writable()); @@ -220,7 +241,7 @@ TEST_CASE("typed anonymous mapping") { } SECTION("moving a memory mapping should work") { - osmium::util::TypedMemoryMapping mapping1(1024); + osmium::util::TypedMemoryMapping mapping1(1000); uint32_t* addr1 = mapping1.get_addr(); *addr1 = 42; @@ -238,8 +259,8 @@ TEST_CASE("typed anonymous mapping") { } SECTION("move assignment should work") { - osmium::util::TypedMemoryMapping mapping1(1024); - osmium::util::TypedMemoryMapping mapping2(1024); + osmium::util::TypedMemoryMapping mapping1(1000); + osmium::util::TypedMemoryMapping mapping2(1000); REQUIRE(!!mapping1); REQUIRE(!!mapping2); @@ -260,26 +281,26 @@ TEST_CASE("typed anonymous mapping") { #ifdef __linux__ SECTION("remapping to larger size should work") { - osmium::util::TypedMemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::TypedMemoryMapping mapping(1000); + REQUIRE(mapping.size() >= 1000); auto addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(2048); + mapping.resize(8000); auto addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); } SECTION("remapping to smaller size should work") { - osmium::util::TypedMemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::TypedMemoryMapping mapping(8000); + REQUIRE(mapping.size() >= 8000); auto addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(512); + mapping.resize(500); auto addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); @@ -302,7 +323,7 @@ TEST_CASE("typed file-based mapping") { REQUIRE(mapping.writable()); REQUIRE(!!mapping); - REQUIRE(mapping.size() == 100); + REQUIRE(mapping.size() >= 100); *mapping.get_addr() = 1234; @@ -314,7 +335,7 @@ TEST_CASE("typed file-based mapping") { REQUIRE(!mapping.writable()); REQUIRE(!!mapping); - REQUIRE(mapping.size() == 100); + REQUIRE(mapping.size() >= 100); REQUIRE(*mapping.get_addr() == 1234); mapping.unmap(); @@ -329,7 +350,7 @@ TEST_CASE("typed file-based mapping") { TEST_CASE("anonymous memory mapping class") { SECTION("simple memory mapping should work") { - osmium::util::AnonymousMemoryMapping mapping(1024); + osmium::util::AnonymousMemoryMapping mapping(1000); REQUIRE(mapping.get_addr() != nullptr); volatile int* addr = mapping.get_addr(); @@ -347,26 +368,26 @@ TEST_CASE("anonymous memory mapping class") { #ifdef __linux__ SECTION("remapping to larger size should work") { - osmium::util::AnonymousMemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::AnonymousMemoryMapping mapping(1000); + REQUIRE(mapping.size() >= 1000); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(2048); + mapping.resize(2000); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); } SECTION("remapping to smaller size should work") { - osmium::util::AnonymousMemoryMapping mapping(1024); - REQUIRE(mapping.size() == 1024); + osmium::util::AnonymousMemoryMapping mapping(2000); + REQUIRE(mapping.size() >= 2000); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(512); + mapping.resize(500); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42);