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
/// \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
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
/// 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
#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>
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 {
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.
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
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,
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());
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:
#include <boost/scoped_ptr.hpp>
#include <cstdlib>
+#include <cstring>
#include <limits>
#include <stdexcept>
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());