Browse Source

[2831] supported shrinkToFit().

JINMEI Tatuya 12 years ago
parent
commit
111e2c83f6

+ 2 - 0
src/lib/util/memory_segment.h

@@ -94,6 +94,8 @@ public:
     /// responsibility to meet the restriction.
     virtual void setNamedAddress(const char* name, void* addr) = 0;
     virtual bool clearNamedAddress(const char* name) = 0;
+
+    virtual void shrinkToFit() = 0;
 };
 
 } // namespace util

+ 3 - 0
src/lib/util/memory_segment_local.h

@@ -67,6 +67,9 @@ public:
     virtual void setNamedAddress(const char* name, void* addr);
     virtual bool clearNamedAddress(const char* name);
 
+    /// There's nothing this implementation can do for this.
+    virtual void shrinkToFit() {}
+
 private:
     // allocated_size_ can underflow, wrap around to max size_t (which
     // is unsigned). But because we only do a check against 0 and not a

+ 23 - 0
src/lib/util/memory_segment_mapped.cc

@@ -170,6 +170,29 @@ MemorySegmentMapped::clearNamedAddress(const char* name) {
     return (impl_->base_sgmt_->destroy<offset_ptr<void> >(name));
 }
 
+void
+MemorySegmentMapped::shrinkToFit() {
+    // It appears an assertion failure is triggered within Boost if the size
+    // is too small.  To work this around we'll make it no-op if the size is
+    // already reasonably small.
+    if (getSize() < INITIAL_SIZE) {
+        return;
+    }
+
+    impl_->base_sgmt_.reset();
+    managed_mapped_file::shrink_to_fit(impl_->filename_.c_str());
+    try {
+        // Remap the grown file; this should succeed, but it's not 100%
+        // guaranteed.  If it fails we treat it as if we fail to create
+        // the new segment.
+        impl_->base_sgmt_.reset(
+            new managed_mapped_file(open_only, impl_->filename_.c_str()));
+    } catch (const boost::interprocess::interprocess_exception& ex) {
+        isc_throw(MemorySegmentError,
+                  "remap after shrink failed; segment is now unusable");
+    }
+}
+
 size_t
 MemorySegmentMapped::getSize() const {
     return (impl_->base_sgmt_->get_size());

+ 2 - 0
src/lib/util/memory_segment_mapped.h

@@ -72,6 +72,8 @@ public:
 
     virtual bool clearNamedAddress(const char* name);
 
+    virtual void shrinkToFit();
+
     size_t getSize() const;
 
 private:

+ 18 - 1
src/lib/util/tests/memory_segment_mapped_unittest.cc

@@ -40,7 +40,7 @@ protected:
 
     ~MemorySegmentMappedTest() {
         segment_.reset();
-        boost::interprocess::file_mapping::remove(mapped_file);
+        //boost::interprocess::file_mapping::remove(mapped_file);
     }
 
     // For initialization and for tests after the segment possibly becomes
@@ -262,4 +262,21 @@ TEST_F(MemorySegmentMappedTest, nullDeallocate) {
     EXPECT_TRUE(segment_->allMemoryDeallocated());
 }
 
+TEST_F(MemorySegmentMappedTest, shrink) {
+    segment_->shrinkToFit();
+    // Normally we should be able to expect the resulting size is the smaller
+    // than the initial default size.  It's not really guaranteed by the API,
+    // however, so we may have to disable this check.
+    const size_t shrinked_size = segment_->getSize();
+    EXPECT_GT(DEFAULT_INITIAL_SIZE, shrinked_size);
+
+    // Another shrink shouldn't cause disruption, and the size shouldn't change
+    segment_->shrinkToFit();
+    EXPECT_EQ(shrinked_size, segment_->getSize());
+
+    // Check the segment is still usable after shrink.
+    void* p = segment_->allocate(sizeof(uint32_t));
+    segment_->deallocate(p, sizeof(uint32_t));
+}
+
 }