Browse Source

[2831] added get/set/clearNamedAddress interfaces

JINMEI Tatuya 12 years ago
parent
commit
a3077227a4

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

@@ -34,6 +34,12 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+class MemorySegmentError : public Exception {
+public:
+    MemorySegmentError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// \brief Memory Segment Class
 ///
 /// This class specifies an interface for allocating memory
@@ -75,6 +81,19 @@ public:
     /// \return Returns <code>true</code> if all allocated memory was
     /// deallocated, <code>false</code> otherwise.
     virtual bool allMemoryDeallocated() const = 0;
+
+    virtual void* getNamedAddress(const char* name) = 0;
+
+    /// \brief TBD
+    ///
+    /// \c addr must be 0 (NULL) or an address that belongs to this segment.
+    /// The latter case means it must be the return value of a previous call
+    /// to \c allocate().  The actual implementation is encouraged to detect
+    /// violation of this restriction and signal it with an exception, but
+    /// it's not an API requirement.  It's generally the caller's
+    /// responsibility to meet the restriction.
+    virtual void setNamedAddress(const char* name, void* addr) = 0;
+    virtual bool clearNamedAddress(const char* name) = 0;
 };
 
 } // namespace util

+ 14 - 0
src/lib/util/memory_segment_local.cc

@@ -51,5 +51,19 @@ MemorySegmentLocal::allMemoryDeallocated() const {
     return (allocated_size_ == 0);
 }
 
+void*
+MemorySegmentLocal::getNamedAddress(const char* /*name*/) {
+    return (0);
+}
+
+void
+MemorySegmentLocal::setNamedAddress(const char* /*name*/, void* /*addr*/) {
+}
+
+bool
+MemorySegmentLocal::clearNamedAddress(const char* /*name*/) {
+    return (false);
+}
+
 } // namespace util
 } // namespace isc

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

@@ -63,6 +63,10 @@ public:
     /// deallocated, <code>false</code> otherwise.
     virtual bool allMemoryDeallocated() const;
 
+    virtual void* getNamedAddress(const char* name);
+    virtual void setNamedAddress(const char* name, void* addr);
+    virtual bool clearNamedAddress(const char* name);
+
 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

+ 34 - 4
src/lib/util/memory_segment_mapped.cc

@@ -17,6 +17,7 @@
 #include <boost/scoped_ptr.hpp>
 #include <boost/interprocess/exceptions.hpp>
 #include <boost/interprocess/managed_mapped_file.hpp>
+#include <boost/interprocess/offset_ptr.hpp>
 
 #include <cassert>
 #include <string>
@@ -25,6 +26,8 @@
 using boost::interprocess::managed_mapped_file;
 using boost::interprocess::open_or_create;
 using boost::interprocess::open_only;
+using boost::interprocess::open_read_only;
+using boost::interprocess::offset_ptr;
 
 namespace isc {
 namespace util {
@@ -36,9 +39,11 @@ struct MemorySegmentMapped::Impl {
                                            initial_size))
     {}
 
-    Impl(const std::string& filename) :
+    Impl(const std::string& filename, bool read_only) :
         filename_(filename),
-        base_sgmt_(new managed_mapped_file(open_only, filename.c_str()))
+        base_sgmt_(read_only ?
+                   new managed_mapped_file(open_read_only, filename.c_str()) :
+                   new managed_mapped_file(open_only, filename.c_str()))
     {}
 
     // mapped file; remember it in case we need to grow it.
@@ -52,7 +57,7 @@ MemorySegmentMapped::MemorySegmentMapped(const std::string& filename) :
     impl_(0)
 {
     try {
-        impl_ = new Impl(filename);
+        impl_ = new Impl(filename, true);
     } catch (const boost::interprocess::interprocess_exception& ex) {
         isc_throw(MemorySegmentOpenError,
                   "failed to open mapped memory segment for " << filename
@@ -68,7 +73,7 @@ MemorySegmentMapped::MemorySegmentMapped(const std::string& filename,
         if (create) {
             impl_ = new Impl(filename, initial_size);
         } else {
-            impl_ = new Impl(filename);
+            impl_ = new Impl(filename, false);
         }
     } catch (const boost::interprocess::interprocess_exception& ex) {
         isc_throw(MemorySegmentOpenError,
@@ -134,6 +139,31 @@ MemorySegmentMapped::allMemoryDeallocated() const {
     return (impl_->base_sgmt_->all_memory_deallocated());
 }
 
+void*
+MemorySegmentMapped::getNamedAddress(const char* name) {
+    offset_ptr<void>* storage =
+        impl_->base_sgmt_->find<offset_ptr<void> >(name).first;
+    if (storage) {
+        return (storage->get());
+    }
+    return (0);
+}
+
+void
+MemorySegmentMapped::setNamedAddress(const char* name, void* addr) {
+    if (addr && !impl_->base_sgmt_->belongs_to_segment(addr)) {
+        isc_throw(MemorySegmentError, "out of segment address: " << addr);
+    }
+    offset_ptr<void>* storage =
+        impl_->base_sgmt_->find_or_construct<offset_ptr<void> >(name)();
+    *storage = addr;
+}
+
+bool
+MemorySegmentMapped::clearNamedAddress(const char* name) {
+    return (impl_->base_sgmt_->destroy<offset_ptr<void> >(name));
+}
+
 size_t
 MemorySegmentMapped::getSize() const {
     return (impl_->base_sgmt_->get_size());

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

@@ -62,6 +62,16 @@ public:
 
     virtual bool allMemoryDeallocated() const;
 
+    virtual void* getNamedAddress(const char* name);
+
+    /// \brief TBD
+    ///
+    /// This implementation detects if \c addr is invalid (see the base class
+    /// description) and throws \c MemorySegmentError in that case.
+    virtual void setNamedAddress(const char* name, void* addr);
+
+    virtual bool clearNamedAddress(const char* name);
+
     size_t getSize() const;
 
 private:

+ 78 - 0
src/lib/util/tests/memory_segment_mapped_unittest.cc

@@ -20,6 +20,7 @@
 #include <boost/scoped_ptr.hpp>
 
 #include <cstdlib>
+#include <cstring>
 #include <limits>
 #include <stdexcept>
 
@@ -178,6 +179,83 @@ TEST_F(MemorySegmentMappedTest, badDeallocate) {
     EXPECT_TRUE(segment_->allMemoryDeallocated());
 }
 
+TEST_F(MemorySegmentMappedTest, namedAddress) {
+    // If not exist, null pointer will be returned.
+    EXPECT_EQ(static_cast<void*>(0), segment_->getNamedAddress("test address"));
+
+    // Now set it
+    void* ptr32 = segment_->allocate(sizeof(uint32_t));
+    const uint32_t test_val = 42;
+    std::memcpy(ptr32, &test_val, sizeof(test_val));
+    segment_->setNamedAddress("test address", ptr32);
+
+    // we can now get it; the stored value should be intact.
+    EXPECT_EQ(ptr32, segment_->getNamedAddress("test address"));
+    EXPECT_EQ(test_val, *static_cast<const uint32_t*>(ptr32));
+
+    // Override it.
+    void* ptr16 = segment_->allocate(sizeof(uint16_t));
+    const uint16_t test_val16 = 4200;
+    std::memcpy(ptr16, &test_val16, sizeof(test_val16));
+    segment_->setNamedAddress("test address", ptr16);
+    EXPECT_EQ(ptr16, segment_->getNamedAddress("test address"));
+    EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(ptr16));
+
+    // Clear it.  Then we won't be able to find it any more.
+    EXPECT_TRUE(segment_->clearNamedAddress("test address"));
+    EXPECT_EQ(static_cast<void*>(0), segment_->getNamedAddress("test address"));
+
+    // duplicate attempt of clear will result in false as it doesn't exist.
+    EXPECT_FALSE(segment_->clearNamedAddress("test address"));
+
+    // Setting NULL is okay.
+    segment_->setNamedAddress("null address", 0);
+    EXPECT_EQ(static_cast<void*>(0), segment_->getNamedAddress("null address"));
+
+    // Setting or out-of-segment address is prohibited, and this implementation
+    // detects and rejects it.
+    uint8_t ch = 'A';
+    EXPECT_THROW(segment_->setNamedAddress("local address", &ch),
+                 MemorySegmentError);
+
+    // Set it again and read it in the read-only mode.
+    segment_->setNamedAddress("test address", ptr16);
+    MemorySegmentMapped segment_ro(mapped_file);
+    EXPECT_TRUE(segment_ro.getNamedAddress("test address"));
+    EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(
+                  segment_ro.getNamedAddress("test address")));
+    EXPECT_EQ(static_cast<void*>(0),
+              segment_ro.getNamedAddress("null address"));
+
+    // clean them up all
+    segment_->deallocate(ptr32, sizeof(uint32_t));
+    segment_->deallocate(ptr16, sizeof(uint32_t));
+    EXPECT_TRUE(segment_->clearNamedAddress("test address"));
+    EXPECT_TRUE(segment_->clearNamedAddress("null address"));
+    EXPECT_TRUE(segment_->allMemoryDeallocated());
+}
+
+TEST_F(MemorySegmentMappedTest, violateReadOnly) {
+    // If the segment is opened in the read only mode, modification attempt
+    // will result in crash.
+    EXPECT_DEATH_IF_SUPPORTED({
+            MemorySegmentMapped segment_ro(mapped_file);
+            segment_ro.allocate(16);
+        }, "");
+    EXPECT_DEATH_IF_SUPPORTED({
+            MemorySegmentMapped segment_ro(mapped_file);
+            segment_ro.setNamedAddress("test", 0);
+        }, "");
+    EXPECT_DEATH_IF_SUPPORTED({
+            void* ptr = segment_->allocate(sizeof(uint32_t));
+            segment_->setNamedAddress("test address", ptr);
+            MemorySegmentMapped segment_ro(mapped_file);
+            EXPECT_TRUE(segment_ro.getNamedAddress("test address"));
+            *static_cast<uint32_t*>(
+                segment_ro.getNamedAddress("test address")) = 0;
+        }, "");
+}
+
 /*
 TEST(MemorySegmentLocal, TestNullDeallocate) {
     auto_ptr<MemorySegment> segment(new MemorySegmentLocal());