summaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorTor Egge <Tor.Egge@broadpark.no>2021-02-10 14:48:36 +0100
committerTor Egge <Tor.Egge@broadpark.no>2021-02-10 16:04:30 +0100
commit3f49fca252a0a7ddc5524c0daba04c0ddb86bda2 (patch)
tree5e192374f183c9c6273b0985250d66549b26c849 /vespalib
parent96faf76fdfd71551f52e9d038bad24e8f98bc301 (diff)
Add memory allocator backed by a file.
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/src/tests/alloc/alloc_test.cpp28
-rw-r--r--vespalib/src/vespa/vespalib/util/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp87
-rw-r--r--vespalib/src/vespa/vespalib/util/mmap_file_allocator.h33
4 files changed, 149 insertions, 0 deletions
diff --git a/vespalib/src/tests/alloc/alloc_test.cpp b/vespalib/src/tests/alloc/alloc_test.cpp
index 4dbeba62ee1..056bac58d28 100644
--- a/vespalib/src/tests/alloc/alloc_test.cpp
+++ b/vespalib/src/tests/alloc/alloc_test.cpp
@@ -2,8 +2,10 @@
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/alloc.h>
+#include <vespa/vespalib/util/mmap_file_allocator.h>
#include <vespa/vespalib/util/exceptions.h>
#include <cstddef>
+#include <sys/mman.h>
using namespace vespalib;
using namespace vespalib::alloc;
@@ -279,4 +281,30 @@ TEST("auto alloced mmap alloc can not be shrinked below HUGEPAGE_SIZE/2 + 1 ") {
EXPECT_EQUAL(SZ, buf.size());
}
+TEST("mmap file allocator works")
+{
+ MmapFileAllocator allocator("mmap-file-allocator-dir");
+ auto alloc = Alloc::alloc_with_allocator(&allocator);
+ auto buf = alloc.create(0);
+ EXPECT_EQUAL(0u, allocator.get_end_offset());
+ EXPECT_EQUAL(0u, buf.size());
+ EXPECT_TRUE(buf.get() == nullptr);
+ buf = alloc.create(4);
+ EXPECT_LESS_EQUAL(4u, buf.size());
+ EXPECT_TRUE(buf.get() != nullptr);
+ memcpy(buf.get(), "1st", 4);
+ auto buf2 = alloc.create(5);
+ EXPECT_LESS_EQUAL(5u, buf2.size());
+ EXPECT_TRUE(buf2.get() != nullptr);
+ EXPECT_TRUE(buf.get() != buf2.get());
+ memcpy(buf2.get(), "fine", 5);
+ EXPECT_FALSE(buf.resize_inplace(5));
+ EXPECT_FALSE(buf.resize_inplace(3));
+ EXPECT_NOT_EQUAL(0u, allocator.get_end_offset());
+ int result = msync(buf.get(), buf.size(), MS_SYNC);
+ EXPECT_EQUAL(0, result);
+ result = msync(buf2.get(), buf2.size(), MS_SYNC);
+ EXPECT_EQUAL(0, result);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
index 1e3f7dc44e7..bee95ad22dc 100644
--- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
@@ -31,6 +31,7 @@ vespa_add_library(vespalib_vespalib_util OBJECT
left_right_heap.cpp
lz4compressor.cpp
md5.c
+ mmap_file_allocator.cpp
printable.cpp
priority_queue.cpp
random.cpp
diff --git a/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp
new file mode 100644
index 00000000000..821e3c86a67
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp
@@ -0,0 +1,87 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "mmap_file_allocator.h"
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <cassert>
+
+namespace vespalib::alloc {
+
+namespace {
+
+const size_t page_size = std::max(getpagesize(), 4096);
+
+size_t round_to_page_size(size_t size) {
+ return (size + ((-size) & (page_size - 1)));
+}
+
+}
+
+MmapFileAllocator::MmapFileAllocator(const vespalib::string& dir_name)
+ : _dir_name(dir_name),
+ _file(_dir_name + "/swapfile"),
+ _end_offset(0)
+{
+ mkdir(_dir_name, true);
+ _file.open(O_RDWR | O_CREAT | O_TRUNC, false);
+}
+
+MmapFileAllocator::~MmapFileAllocator()
+{
+ assert(_allocations.empty());
+ _file.close();
+ _file.unlink();
+ rmdir(_dir_name, true);
+}
+
+MmapFileAllocator::PtrAndSize
+MmapFileAllocator::alloc(size_t sz) const
+{
+ if (sz == 0) {
+ return PtrAndSize(nullptr, 0); // empty allocation
+ }
+ uint64_t offset = _end_offset;
+ sz = round_to_page_size(sz);
+ _end_offset += sz;
+ _file.resize(_end_offset);
+ void *buf = mmap(nullptr, sz,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ _file.getFileDescriptor(),
+ offset);
+ assert(buf != MAP_FAILED);
+ assert(buf != nullptr);
+ // Register allocation
+ auto ins_res = _allocations.emplace(buf, sz);
+ assert(ins_res.second);
+ return PtrAndSize(buf, sz);
+}
+
+
+void
+MmapFileAllocator::free(PtrAndSize alloc) const
+{
+ if (alloc.second == 0) {
+ assert(alloc.first == nullptr);
+ return; // empty allocation
+ }
+ assert(alloc.first != nullptr);
+ // Check that matching allocation is registered
+ auto itr = _allocations.find(alloc.first);
+ assert(itr != _allocations.end());
+ assert(itr->first == alloc.first);
+ assert(itr->second == alloc.second);
+ _allocations.erase(itr);
+ int retval = madvise(alloc.first, alloc.second, MADV_DONTNEED);
+ assert(retval == 0);
+ retval = munmap(alloc.first, alloc.second);
+ assert(retval == 0);
+}
+
+size_t
+MmapFileAllocator::resize_inplace(PtrAndSize, size_t) const
+{
+ return 0;
+}
+
+}
diff --git a/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h
new file mode 100644
index 00000000000..84f63c8fc61
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h
@@ -0,0 +1,33 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "alloc.h"
+#include <vespa/vespalib/io/fileutil.h>
+#include <vespa/vespalib/stllike/string.h>
+#include <map>
+
+namespace vespalib::alloc {
+
+/*
+ * Class handling memory allocations backed by one or more files.
+ * Not reentant. Should not be destructed before all allocations
+ * have been freed.
+ */
+class MmapFileAllocator : public MemoryAllocator {
+ vespalib::string _dir_name;
+ mutable File _file;
+ mutable uint64_t _end_offset;
+ mutable std::map<void *, size_t> _allocations;
+public:
+ MmapFileAllocator(const vespalib::string& dir_name);
+ ~MmapFileAllocator();
+ PtrAndSize alloc(size_t sz) const override;
+ void free(PtrAndSize alloc) const override;
+ size_t resize_inplace(PtrAndSize, size_t) const override;
+
+ // For unit test
+ size_t get_end_offset() const noexcept { return _end_offset; }
+};
+
+}