Browse Source

[2851] Merge branch 'trac2850' into trac2851

JINMEI Tatuya 12 years ago
parent
commit
721708db65
28 changed files with 1099 additions and 300 deletions
  1. 3 0
      configure.ac
  2. 9 8
      src/lib/datasrc/client_list.cc
  3. 6 2
      src/lib/datasrc/memory/Makefile.am
  4. 10 0
      src/lib/datasrc/memory/zone_table_segment.cc
  5. 128 13
      src/lib/datasrc/memory/zone_table_segment.h
  6. 17 9
      src/lib/datasrc/memory/zone_table_segment_local.cc
  7. 19 4
      src/lib/datasrc/memory/zone_table_segment_local.h
  8. 363 0
      src/lib/datasrc/memory/zone_table_segment_mapped.cc
  9. 130 0
      src/lib/datasrc/memory/zone_table_segment_mapped.h
  10. 22 19
      src/lib/datasrc/memory/zone_writer_local.cc
  11. 45 16
      src/lib/datasrc/memory/zone_writer.h
  12. 0 95
      src/lib/datasrc/memory/zone_writer_local.h
  13. 2 1
      src/lib/datasrc/tests/client_list_unittest.cc
  14. 6 0
      src/lib/datasrc/tests/memory/Makefile.am
  15. 6 4
      src/lib/datasrc/tests/memory/zone_loader_util.cc
  16. 192 0
      src/lib/datasrc/tests/memory/zone_table_segment_mapped_unittest.cc
  17. 12 46
      src/lib/datasrc/tests/memory/zone_table_segment_test.h
  18. 19 18
      src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
  19. 44 18
      src/lib/datasrc/tests/memory/zone_writer_unittest.cc
  20. 3 2
      src/lib/datasrc/tests/zone_finder_context_unittest.cc
  21. 3 3
      src/lib/datasrc/tests/zone_loader_unittest.cc
  22. 12 4
      src/lib/util/memory_segment.h
  23. 3 3
      src/lib/util/memory_segment_local.cc
  24. 1 1
      src/lib/util/memory_segment_local.h
  25. 3 3
      src/lib/util/memory_segment_mapped.cc
  26. 1 1
      src/lib/util/memory_segment_mapped.h
  27. 8 8
      src/lib/util/tests/memory_segment_common_unittest.cc
  28. 32 22
      src/lib/util/tests/memory_segment_mapped_unittest.cc

+ 3 - 0
configure.ac

@@ -891,6 +891,9 @@ if test X$use_shared_memory = Xyes -a "$BOOST_MAPPED_FILE_WOULDFAIL" = "yes"; th
     AC_MSG_ERROR([Boost shared memory does not compile on this system.  If you don't need it (most normal users won't) build without it by rerunning this script with --without-shared-memory; using a different compiler or a different version of Boost may also help.])
     AC_MSG_ERROR([Boost shared memory does not compile on this system.  If you don't need it (most normal users won't) build without it by rerunning this script with --without-shared-memory; using a different compiler or a different version of Boost may also help.])
 fi
 fi
 AM_CONDITIONAL([USE_SHARED_MEMORY], [test x$use_shared_memory = xyes])
 AM_CONDITIONAL([USE_SHARED_MEMORY], [test x$use_shared_memory = xyes])
+if test "x$use_shared_memory" = "xyes"; then
+    AC_DEFINE(USE_SHARED_MEMORY, 1, [Define to 1 if shared memory support is enabled])
+fi
 AC_SUBST(BOOST_MAPPED_FILE_CXXFLAG)
 AC_SUBST(BOOST_MAPPED_FILE_CXXFLAG)
 
 
 # Add some default CPP flags needed for Boost, identified by the AX macro.
 # Add some default CPP flags needed for Boost, identified by the AX macro.

+ 9 - 8
src/lib/datasrc/client_list.cc

@@ -153,13 +153,13 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                               << zname << "/" << rrclass_);
                               << zname << "/" << rrclass_);
                 }
                 }
                 assert(load_action); // in this loop this should be always true
                 assert(load_action); // in this loop this should be always true
-                boost::scoped_ptr<memory::ZoneWriter> writer;
                 try {
                 try {
-                    writer.reset(new_data_sources.back().ztable_segment_->
-                                 getZoneWriter(load_action, zname, rrclass_));
-                    writer->load();
-                    writer->install();
-                    writer->cleanup();
+                    memory::ZoneWriter writer(
+                        *new_data_sources.back().ztable_segment_,
+                        load_action, zname, rrclass_);
+                    writer.load();
+                    writer.install();
+                    writer.cleanup();
                 } catch (const ZoneLoaderException& e) {
                 } catch (const ZoneLoaderException& e) {
                     LOG_ERROR(logger, DATASRC_LOAD_ZONE_ERROR)
                     LOG_ERROR(logger, DATASRC_LOAD_ZONE_ERROR)
                         .arg(zname).arg(rrclass_).arg(name).arg(e.what());
                         .arg(zname).arg(rrclass_).arg(name).arg(e.what());
@@ -348,8 +348,9 @@ ConfigurableClientList::getCachedZoneWriter(const Name& name) {
     }
     }
     return (ZoneWriterPair(ZONE_SUCCESS,
     return (ZoneWriterPair(ZONE_SUCCESS,
                            ZoneWriterPtr(
                            ZoneWriterPtr(
-                               result.info->ztable_segment_->
-                               getZoneWriter(load_action, name, rrclass_))));
+                               new memory::ZoneWriter(
+                                   *result.info->ztable_segment_,
+                                   load_action, name, rrclass_))));
 }
 }
 
 
 // NOTE: This function is not tested, it would be complicated. However, the
 // NOTE: This function is not tested, it would be complicated. However, the

+ 6 - 2
src/lib/datasrc/memory/Makefile.am

@@ -22,11 +22,15 @@ libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
 libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
 libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
 libdatasrc_memory_la_SOURCES += zone_table_segment.h zone_table_segment.cc
 libdatasrc_memory_la_SOURCES += zone_table_segment.h zone_table_segment.cc
 libdatasrc_memory_la_SOURCES += zone_table_segment_local.h zone_table_segment_local.cc
 libdatasrc_memory_la_SOURCES += zone_table_segment_local.h zone_table_segment_local.cc
+
+if USE_SHARED_MEMORY
+libdatasrc_memory_la_SOURCES += zone_table_segment_mapped.h zone_table_segment_mapped.cc
+endif
+
 libdatasrc_memory_la_SOURCES += zone_data_updater.h zone_data_updater.cc
 libdatasrc_memory_la_SOURCES += zone_data_updater.h zone_data_updater.cc
 libdatasrc_memory_la_SOURCES += zone_data_loader.h zone_data_loader.cc
 libdatasrc_memory_la_SOURCES += zone_data_loader.h zone_data_loader.cc
 libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
 libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
-libdatasrc_memory_la_SOURCES += zone_writer.h
-libdatasrc_memory_la_SOURCES += zone_writer_local.h zone_writer_local.cc
+libdatasrc_memory_la_SOURCES += zone_writer.h zone_writer.cc
 libdatasrc_memory_la_SOURCES += load_action.h
 libdatasrc_memory_la_SOURCES += load_action.h
 libdatasrc_memory_la_SOURCES += util_internal.h
 libdatasrc_memory_la_SOURCES += util_internal.h
 
 

+ 10 - 0
src/lib/datasrc/memory/zone_table_segment.cc

@@ -12,8 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include "config.h"
+
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_table_segment_local.h>
+#ifdef USE_SHARED_MEMORY
+#include <datasrc/memory/zone_table_segment_mapped.h>
+#endif
+#include <datasrc/memory/zone_writer.h>
 
 
 #include <string>
 #include <string>
 
 
@@ -30,6 +36,10 @@ ZoneTableSegment::create(const RRClass& rrclass, const std::string& type) {
     // Until that it becomes a real issue we won't be too smart.
     // Until that it becomes a real issue we won't be too smart.
     if (type == "local") {
     if (type == "local") {
         return (new ZoneTableSegmentLocal(rrclass));
         return (new ZoneTableSegmentLocal(rrclass));
+#ifdef USE_SHARED_MEMORY
+    } else if (type == "mapped") {
+        return (new ZoneTableSegmentMapped(rrclass));
+#endif
     }
     }
     isc_throw(UnknownSegmentType, "Zone table segment type not supported: "
     isc_throw(UnknownSegmentType, "Zone table segment type not supported: "
               << type);
               << type);

+ 128 - 13
src/lib/datasrc/memory/zone_table_segment.h

@@ -49,6 +49,30 @@ public:
     {}
     {}
 };
 };
 
 
+/// \brief Exception thrown when a \c reset() on a ZoneTableSegment
+/// fails (due to various reasons). When this exception is thrown, there
+/// is still a strong guarantee that the previously existing backing
+/// memory store was not unloaded.
+class ResetFailed : public isc::Exception {
+public:
+    ResetFailed(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Exception thrown when a \c reset() on a ZoneTableSegment
+/// fails (due to various reasons), and it was not able to preserve any
+/// existing backing memory store. When this exception is thrown, there
+/// is a strong guarantee that the previously existing backing memory
+/// store was cleared.
+class ResetFailedAndSegmentCleared : public isc::Exception {
+public:
+    ResetFailedAndSegmentCleared(const char* file, size_t line,
+				 const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
 /// \brief Memory-management independent entry point that contains a
 /// \brief Memory-management independent entry point that contains a
 /// pointer to a zone table in memory.
 /// pointer to a zone table in memory.
 ///
 ///
@@ -89,21 +113,46 @@ protected:
     /// An instance implementing this interface is expected to be
     /// An instance implementing this interface is expected to be
     /// created by the factory method (\c create()), so this constructor
     /// created by the factory method (\c create()), so this constructor
     /// is protected.
     /// is protected.
-    ZoneTableSegment(isc::dns::RRClass)
+    ZoneTableSegment(const isc::dns::RRClass&)
     {}
     {}
 public:
 public:
     /// \brief Destructor
     /// \brief Destructor
     virtual ~ZoneTableSegment() {}
     virtual ~ZoneTableSegment() {}
 
 
     /// \brief Return the ZoneTableHeader for the zone table segment.
     /// \brief Return the ZoneTableHeader for the zone table segment.
+    ///
+    /// \throw isc::InvalidOperation may be thrown by some
+    /// implementations if this method is called without calling
+    /// \c reset() successfully first.
     virtual ZoneTableHeader& getHeader() = 0;
     virtual ZoneTableHeader& getHeader() = 0;
 
 
     /// \brief const version of \c getHeader().
     /// \brief const version of \c getHeader().
+    ///
+    /// \throw isc::InvalidOperation may be thrown by some
+    /// implementations if this method is called without calling
+    /// \c reset() successfully first.
     virtual const ZoneTableHeader& getHeader() const = 0;
     virtual const ZoneTableHeader& getHeader() const = 0;
 
 
     /// \brief Return the MemorySegment for the zone table segment.
     /// \brief Return the MemorySegment for the zone table segment.
+    ///
+    /// \throw isc::InvalidOperation may be thrown by some
+    /// implementations if this method is called without calling
+    /// \c reset() successfully first.
     virtual isc::util::MemorySegment& getMemorySegment() = 0;
     virtual isc::util::MemorySegment& getMemorySegment() = 0;
 
 
+    /// \brief Return true if the segment is writable.
+    ///
+    /// The user of the zone table segment will load or update zones
+    /// into the segment only for writable ones.  The precise definition
+    /// of "writability" differs in different derived classes (see
+    /// derived class documentation).  In general, however, the user
+    /// should only rely on this interface rather than assume a specific
+    /// definition for a specific type of segment.
+    ///
+    /// \throw None This method's implementations must be
+    /// exception-free.
+    virtual bool isWritable() const = 0;
+
     /// \brief Create an instance depending on the memory segment model
     /// \brief Create an instance depending on the memory segment model
     ///
     ///
     /// This is a factory method to create a derived ZoneTableSegment
     /// This is a factory method to create a derived ZoneTableSegment
@@ -128,20 +177,86 @@ public:
     /// \param segment The segment to destroy.
     /// \param segment The segment to destroy.
     static void destroy(ZoneTableSegment* segment);
     static void destroy(ZoneTableSegment* segment);
 
 
-    /// \brief Create a zone write corresponding to this segment
+    /// \brief The mode using which to open a ZoneTableSegment.
+    ///
+    /// - CREATE: If the backing memory store doesn't exist, create
+    ///           it. If it exists, overwrite it with a newly created
+    ///           memory store. In both cases, open the newly created
+    ///           memory store in read+write mode.
+    ///
+    /// - READ_WRITE: If the backing memory store doesn't exist, create
+    ///               it. If it exists, use the existing memory store
+    ///               as-is. In both cases, open the memory store in
+    ///               read+write mode.
+    ///
+    /// - READ_ONLY: If the backing memory store doesn't exist, throw an
+    ///              exception. If it exists, open the existing memory
+    ///              store in read-only mode.
+    enum MemorySegmentOpenMode {
+        CREATE,
+        READ_WRITE,
+        READ_ONLY
+    };
+
+    /// \brief Unload the current memory store (if loaded) and load the
+    /// specified one.
+    ///
+    /// In case opening/loading the new memory store fails for some
+    /// reason, one of the following documented (further below)
+    /// exceptions may be thrown. In case failures occur,
+    /// implementations of this method must strictly provide the
+    /// associated behavior as follows, and in the exception
+    /// documentation below.  Code that uses \c ZoneTableSegment would
+    /// depend on such assurances.
+    ///
+    /// In case an existing memory store is in use, and an attempt to
+    /// load a different memory store fails, the existing memory store
+    /// must still be available and the \c ResetFailed exception must be
+    /// thrown. In this case, the segment is still usable.
+    ///
+    /// In case an existing memory store is in use, and an attempt is
+    /// made to reload the same memory store which results in a failure,
+    /// the existing memory store must no longer be available and the
+    /// \c ResetFailedAndSegmentCleared exception must be thrown. In
+    /// this case, the segment is no longer usable without a further
+    /// successful call to \c reset().
+    ///
+    /// See the \c MemorySegmentOpenMode documentation above for the
+    /// various modes in which a ZoneTableSegment can be created.
+    ///
+    /// \c params should contain an implementation-defined
+    /// configuration. See the specific \c ZoneTableSegment
+    /// implementation class for details of what to pass in this
+    /// argument.
+    ///
+    /// \throws isc::InvalidParameter if the configuration in \c params
+    /// has incorrect syntax, but the segment is still usable due to the
+    /// old memory store still being in use.
+    ///
+    /// \throw ResetFailed if there was a problem in loading the new
+    /// memory store, but the segment is still usable due to the old
+    /// memory store still being in use.
+    ///
+    /// \throw ResetFailedAndSegmentCleared if there was a problem in
+    /// loading the new memory store, but the old memory store was also
+    /// unloaded and is no longer in use. The segment is not usable
+    /// without a further successful \c reset().
+    ///
+    /// \param mode The open mode (see the MemorySegmentOpenMode
+    /// documentation).
+    /// \param params An element containing implementation-specific
+    /// config (see the description).
+    virtual void reset(MemorySegmentOpenMode mode,
+                       isc::data::ConstElementPtr params) = 0;
+
+    /// \brief Unload the current memory store (if loaded).
     ///
     ///
-    /// This creates a new write that can be used to update zones
-    /// inside this zone table segment.
+    /// Implementations of this method should unload any current memory
+    /// store and reset the `ZoneTableSegment` to a freshly constructed
+    /// state.
     ///
     ///
-    /// \param loadAction Callback to provide the actual data.
-    /// \param origin The origin of the zone to reload.
-    /// \param rrclass The class of the zone to reload.
-    /// \return New instance of a zone writer. The ownership is passed
-    ///     onto the caller and the caller needs to \c delete it when
-    ///     it's done with the writer.
-    virtual ZoneWriter* getZoneWriter(const LoadAction& load_action,
-                                      const dns::Name& origin,
-                                      const dns::RRClass& rrclass) = 0;
+    /// \throw none
+    virtual void clear() = 0;
 };
 };
 
 
 } // namespace memory
 } // namespace memory

+ 17 - 9
src/lib/datasrc/memory/zone_table_segment_local.cc

@@ -13,7 +13,6 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_table_segment_local.h>
-#include "zone_writer_local.h"
 
 
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::util;
@@ -38,6 +37,23 @@ ZoneTableSegmentLocal::~ZoneTableSegmentLocal() {
     assert(mem_sgmt_.allMemoryDeallocated());
     assert(mem_sgmt_.allMemoryDeallocated());
 }
 }
 
 
+void
+ZoneTableSegmentLocal::reset(MemorySegmentOpenMode,
+                             isc::data::ConstElementPtr)
+{
+    isc_throw(isc::NotImplemented,
+              "ZoneTableSegmentLocal::reset() is not implemented and "
+              "should not be used.");
+}
+
+void
+ZoneTableSegmentLocal::clear()
+{
+    isc_throw(isc::NotImplemented,
+              "ZoneTableSegmentLocal::clear() is not implemented and "
+              "should not be used.");
+}
+
 // After more methods' definitions are added here, it would be a good
 // After more methods' definitions are added here, it would be a good
 // idea to move getHeader() and getMemorySegment() definitions to the
 // idea to move getHeader() and getMemorySegment() definitions to the
 // header file.
 // header file.
@@ -56,14 +72,6 @@ ZoneTableSegmentLocal::getMemorySegment() {
      return (mem_sgmt_);
      return (mem_sgmt_);
 }
 }
 
 
-ZoneWriter*
-ZoneTableSegmentLocal::getZoneWriter(const LoadAction& load_action,
-                                     const dns::Name& name,
-                                     const dns::RRClass& rrclass)
-{
-    return (new ZoneWriterLocal(this, load_action, name, rrclass));
-}
-
 } // namespace memory
 } // namespace memory
 } // namespace datasrc
 } // namespace datasrc
 } // namespace isc
 } // namespace isc

+ 19 - 4
src/lib/datasrc/memory/zone_table_segment_local.h

@@ -53,10 +53,25 @@ public:
     /// implementation (a MemorySegmentLocal instance).
     /// implementation (a MemorySegmentLocal instance).
     virtual isc::util::MemorySegment& getMemorySegment();
     virtual isc::util::MemorySegment& getMemorySegment();
 
 
-    /// \brief Concrete implementation of ZoneTableSegment::getZoneWriter
-    virtual ZoneWriter* getZoneWriter(const LoadAction& load_action,
-                                      const dns::Name& origin,
-                                      const dns::RRClass& rrclass);
+    /// \brief Return true if the segment is writable.
+    ///
+    /// Local segments are always writable. This implementation always
+    /// returns true.
+    virtual bool isWritable() const {
+        return (true);
+    }
+
+    /// \brief This method is not implemented.
+    ///
+    /// \throw isc::NotImplemented
+    virtual void reset(MemorySegmentOpenMode mode,
+                       isc::data::ConstElementPtr params);
+
+    /// \brief This method is not implemented.
+    ///
+    /// \throw isc::NotImplemented
+    virtual void clear();
+
 private:
 private:
     isc::util::MemorySegmentLocal mem_sgmt_;
     isc::util::MemorySegmentLocal mem_sgmt_;
     ZoneTableHeader header_;
     ZoneTableHeader header_;

+ 363 - 0
src/lib/datasrc/memory/zone_table_segment_mapped.cc

@@ -0,0 +1,363 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment_mapped.h>
+
+#include <memory>
+
+using namespace isc::data;
+using namespace isc::dns;
+using namespace isc::util;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace { // unnamed namespace
+
+// The name with which the zone table checksum is associated in the segment.
+const char* const ZONE_TABLE_CHECKSUM_NAME = "zone_table_checksum";
+
+// The name with which the zone table header is associated in the segment.
+const char* const ZONE_TABLE_HEADER_NAME = "zone_table_header";
+
+} // end of unnamed namespace
+
+ZoneTableSegmentMapped::ZoneTableSegmentMapped(const RRClass& rrclass) :
+    ZoneTableSegment(rrclass),
+    rrclass_(rrclass),
+    header_(NULL)
+{
+}
+
+bool
+ZoneTableSegmentMapped::processChecksum(MemorySegmentMapped& segment,
+                                        bool create,
+                                        std::string& error_msg)
+{
+    MemorySegment::NamedAddressResult result =
+        segment.getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
+    if (result.first) {
+        if (create) {
+            // There must be no previously saved checksum.
+            error_msg = "There is already a saved checksum in the segment "
+                 "opened in create mode";
+            return (false);
+        } else {
+            // The segment was already shrunk when it was last
+            // closed. Check that its checksum is consistent.
+            assert(result.second);
+            uint32_t* checksum = static_cast<uint32_t*>(result.second);
+            const uint32_t saved_checksum = *checksum;
+            // First, clear the checksum so that getCheckSum() returns a
+            // consistent value.
+            *checksum = 0;
+            const uint32_t new_checksum = segment.getCheckSum();
+            if (saved_checksum != new_checksum) {
+                error_msg = "Saved checksum doesn't match segment data";
+                return (false);
+            }
+        }
+    } else {
+        // Allocate space for a checksum (which is saved during close).
+
+        // First allocate a ZONE_TABLE_CHECKSUM_NAME, so that we can set
+        // it without growing the segment (and changing the checksum's
+        // address).
+        segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME, NULL);
+        void* checksum = NULL;
+        while (!checksum) {
+            try {
+                checksum = segment.allocate(sizeof(uint32_t));
+            } catch (const MemorySegmentGrown&) {
+                // Do nothing and try again.
+            }
+        }
+        *static_cast<uint32_t*>(checksum) = 0;
+        const bool grew = segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME,
+                                                  checksum);
+        if (grew) {
+            // If the segment grew here, we have a problem as the
+            // checksum address may no longer be valid. In this case, we
+            // cannot recover. This case is extremely unlikely as we
+            // reserved memory for the ZONE_TABLE_CHECKSUM_NAME
+            // above. It indicates a very restrictive MemorySegment
+            // which we should not use.
+            error_msg = "Segment grew unexpectedly in setNamedAddress()";
+            return (false);
+        }
+    }
+
+    return (true);
+}
+
+bool
+ZoneTableSegmentMapped::processHeader(MemorySegmentMapped& segment,
+                                      bool create,
+                                      std::string& error_msg)
+{
+    MemorySegment::NamedAddressResult result =
+        segment.getNamedAddress(ZONE_TABLE_HEADER_NAME);
+    if (result.first) {
+        if (create) {
+            // There must be no previously saved checksum.
+            error_msg = "There is already a saved ZoneTableHeader in the "
+                 "segment opened in create mode";
+            return (false);
+        } else {
+            assert(result.second);
+            header_ = static_cast<ZoneTableHeader*>(result.second);
+        }
+    } else {
+        // First allocate a ZONE_TABLE_HEADER_NAME, so that we can set
+        // it without growing the segment (and changing the header's
+        // address).
+        segment.setNamedAddress(ZONE_TABLE_HEADER_NAME, NULL);
+        void* ptr = NULL;
+        while (!ptr) {
+            try {
+                ptr = segment.allocate(sizeof(ZoneTableHeader));
+            } catch (const MemorySegmentGrown&) {
+                // Do nothing and try again.
+            }
+        }
+        ZoneTableHeader* new_header = new(ptr)
+            ZoneTableHeader(ZoneTable::create(segment, rrclass_));
+        const bool grew = segment.setNamedAddress(ZONE_TABLE_HEADER_NAME,
+                                                  new_header);
+        if (grew) {
+            // If the segment grew here, we have a problem as the table
+            // header address may no longer be valid. In this case, we
+            // cannot recover. This case is extremely unlikely as we
+            // reserved memory for the ZONE_TABLE_HEADER_NAME above. It
+            // indicates a very restrictive MemorySegment which we
+            // should not use.
+            error_msg = "Segment grew unexpectedly in setNamedAddress()";
+            return (false);
+        }
+        header_ = new_header;
+    }
+
+    return (true);
+}
+
+void
+ZoneTableSegmentMapped::openReadWrite(const std::string& filename,
+                                      bool create)
+{
+    const MemorySegmentMapped::OpenMode mode = create ?
+         MemorySegmentMapped::CREATE_ONLY :
+         MemorySegmentMapped::OPEN_OR_CREATE;
+    // In case there is a problem, we throw. We want the segment to be
+    // automatically destroyed then.
+    std::auto_ptr<MemorySegmentMapped> segment
+        (new MemorySegmentMapped(filename, mode));
+
+    std::string error_msg;
+    if (!processChecksum(*segment, create, error_msg)) {
+         if (mem_sgmt_) {
+              isc_throw(ResetFailed,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         } else {
+              isc_throw(ResetFailedAndSegmentCleared,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         }
+    }
+
+    if (!processHeader(*segment, create, error_msg)) {
+         if (mem_sgmt_) {
+              isc_throw(ResetFailed,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         } else {
+              isc_throw(ResetFailedAndSegmentCleared,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         }
+    }
+
+    mem_sgmt_.reset(segment.release());
+}
+
+void
+ZoneTableSegmentMapped::openReadOnly(const std::string& filename) {
+    // In case there is a checksum mismatch, we throw. We want the
+    // segment to be automatically destroyed then.
+    std::auto_ptr<MemorySegmentMapped> segment
+        (new MemorySegmentMapped(filename));
+    // There must be a previously saved checksum.
+    MemorySegment::NamedAddressResult result =
+        segment->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
+    if (!result.first) {
+         const std::string error_msg =
+             "There is no previously saved checksum in a "
+             "mapped segment opened in read-only mode";
+         if (mem_sgmt_) {
+              isc_throw(ResetFailed,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         } else {
+              isc_throw(ResetFailedAndSegmentCleared,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         }
+    }
+
+    // We can't verify the checksum here as we can't set the checksum to
+    // 0 for checksum calculation in a read-only segment. So we continue
+    // without verifying the checksum.
+
+    // There must be a previously saved ZoneTableHeader.
+    result = segment->getNamedAddress(ZONE_TABLE_HEADER_NAME);
+    if (result.first) {
+        assert(result.second);
+        header_ = static_cast<ZoneTableHeader*>(result.second);
+    } else {
+         const std::string error_msg =
+             "There is no previously saved ZoneTableHeader in a "
+             "mapped segment opened in read-only mode.";
+         if (mem_sgmt_) {
+              isc_throw(ResetFailed,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         } else {
+              isc_throw(ResetFailedAndSegmentCleared,
+                        "Error in resetting zone table segment to use "
+                        << filename << ": " << error_msg);
+         }
+    }
+
+    mem_sgmt_.reset(segment.release());
+}
+
+void
+ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
+                              isc::data::ConstElementPtr params)
+{
+    if (!params || params->getType() != Element::map) {
+        isc_throw(isc::InvalidParameter,
+                  "Configuration does not contain a map");
+    }
+
+    if (!params->contains("mapped-file")) {
+        isc_throw(isc::InvalidParameter,
+                  "Configuration does not contain a \"mapped-file\" key");
+    }
+
+    ConstElementPtr mapped_file = params->get("mapped-file");
+    if ((!mapped_file) || (mapped_file->getType() != Element::string)) {
+        isc_throw(isc::InvalidParameter,
+                  "Value of \"mapped-file\" is not a string");
+    }
+
+    const std::string filename = mapped_file->stringValue();
+
+    if (mem_sgmt_ && (filename == current_filename_)) {
+        // This reset() is an attempt to re-open the currently open
+        // mapped file. We cannot do this in many mode combinations
+        // unless we close the existing mapped file. So just close it.
+        clear();
+    }
+
+    switch (mode) {
+    case CREATE:
+        openReadWrite(filename, true);
+        break;
+
+    case READ_WRITE:
+        openReadWrite(filename, false);
+        break;
+
+    case READ_ONLY:
+        openReadOnly(filename);
+    }
+
+    current_mode_ = mode;
+    current_filename_ = filename;
+}
+
+void
+ZoneTableSegmentMapped::clear()
+{
+    if (mem_sgmt_) {
+        if (isWritable()) {
+            // If there is a previously opened segment, and it was
+            // opened in read-write mode, update its checksum.
+            mem_sgmt_->shrinkToFit();
+            const MemorySegment::NamedAddressResult result =
+                mem_sgmt_->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
+            assert(result.first);
+            assert(result.second);
+            uint32_t* checksum = static_cast<uint32_t*>(result.second);
+            // First, clear the checksum so that getCheckSum() returns
+            // a consistent value.
+            *checksum = 0;
+            const uint32_t new_checksum = mem_sgmt_->getCheckSum();
+            // Now, update it into place.
+            *checksum = new_checksum;
+        }
+        // Close the segment here in case the code further below
+        // doesn't complete successfully.
+        header_ = NULL;
+        mem_sgmt_.reset();
+    }
+}
+
+// After more methods' definitions are added here, it would be a good
+// idea to move getHeader() and getMemorySegment() definitions to the
+// header file.
+ZoneTableHeader&
+ZoneTableSegmentMapped::getHeader() {
+    if (!mem_sgmt_) {
+        isc_throw(isc::InvalidOperation,
+                  "getHeader() called without calling reset() first");
+    }
+    return (*header_);
+}
+
+const ZoneTableHeader&
+ZoneTableSegmentMapped::getHeader() const {
+    if (!mem_sgmt_) {
+        isc_throw(isc::InvalidOperation,
+                  "getHeader() called without calling reset() first");
+    }
+    return (*header_);
+}
+
+MemorySegment&
+ZoneTableSegmentMapped::getMemorySegment() {
+    if (!mem_sgmt_) {
+        isc_throw(isc::InvalidOperation,
+                  "getMemorySegment() called without calling reset() first");
+    }
+    return (*mem_sgmt_);
+}
+
+bool
+ZoneTableSegmentMapped::isWritable() const {
+    if (!mem_sgmt_) {
+        // If reset() was never performed for this segment, or if the
+        // most recent reset() had failed, then the segment is not
+        // writable.
+        return (false);
+    }
+
+    return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc

+ 130 - 0
src/lib/datasrc/memory/zone_table_segment_mapped.h

@@ -0,0 +1,130 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ZONE_TABLE_SEGMENT_MAPPED_H
+#define ZONE_TABLE_SEGMENT_MAPPED_H
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <util/memory_segment_mapped.h>
+
+#include <boost/scoped_ptr.hpp>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Mapped-file based implementation of ZoneTableSegment class
+///
+/// This class specifies a concrete implementation for a memory-mapped
+/// ZoneTableSegment. Please see the ZoneTableSegment class
+/// documentation for usage.
+class ZoneTableSegmentMapped : public ZoneTableSegment {
+    // This is so that ZoneTableSegmentMapped can be instantiated from
+    // ZoneTableSegment::create().
+    friend class ZoneTableSegment;
+
+protected:
+    /// \brief Protected constructor
+    ///
+    /// Instances are expected to be created by the factory method
+    /// (\c ZoneTableSegment::create()), so this constructor is
+    /// protected.
+    ZoneTableSegmentMapped(const isc::dns::RRClass& rrclass);
+
+public:
+    /// \brief Return the ZoneTableHeader for the mapped zone table
+    /// segment implementation.
+    ///
+    /// \throws isc::InvalidOperation if this method is called without a
+    /// successful \c reset() call first.
+    virtual ZoneTableHeader& getHeader();
+
+    /// \brief const version of \c getHeader().
+    ///
+    /// \throws isc::InvalidOperation if this method is called without a
+    /// successful \c reset() call first.
+    virtual const ZoneTableHeader& getHeader() const;
+
+    /// \brief Return the MemorySegment for the memory-mapped zone table
+    /// segment implementation (a MemorySegmentMapped instance).
+    ///
+    /// \throws isc::InvalidOperation if this method is called without a
+    /// successful \c reset() call first.
+    virtual isc::util::MemorySegment& getMemorySegment();
+
+    /// \brief Returns if the segment is writable.
+    ///
+    /// Segments successfully opened in CREATE or READ_WRITE modes are
+    /// writable. Segments opened in READ_ONLY mode are not writable.
+    /// If there was a failure in \c reset(), the segment is not
+    /// writable.
+    virtual bool isWritable() const;
+
+    /// \brief Unmap the current file (if mapped) and map the specified
+    /// one.
+    ///
+    /// In case of exceptions, the current existing mapped file may be
+    /// left open, or may be cleared. Please see the \c ZoneTableSegment
+    /// API documentation for the behavior.
+    ///
+    /// See the \c MemorySegmentOpenMode documentation (in
+    /// \c ZoneTableSegment class) for the various modes in which a
+    /// \c ZoneTableSegmentMapped can be created.
+    ///
+    /// \c params should be a map containing a "mapped-file" key that
+    /// points to a string value containing the filename of a mapped
+    /// file. E.g.,
+    ///
+    ///  {"mapped-file": "/var/bind10/mapped-files/zone-sqlite3.mapped.0"}
+    ///
+    /// \throws isc::InvalidParameter if the configuration in \c params
+    /// has incorrect syntax.
+    /// \throws isc::Unexpected for a variety of cases where an
+    /// unexpected condition occurs. These should not occur normally in
+    /// correctly written code.
+    ///
+    /// \param mode The open mode (see the \c MemorySegmentOpenMode
+    /// documentation in \c ZoneTableSegment class).
+    /// \param params An element containing config for the mapped file
+    /// (see the description).
+    virtual void reset(MemorySegmentOpenMode mode,
+                       isc::data::ConstElementPtr params);
+
+    /// \brief Unmap the current file (if mapped).
+    virtual void clear();
+
+private:
+    bool processChecksum(isc::util::MemorySegmentMapped& segment, bool create,
+                         std::string& error_msg);
+    bool processHeader(isc::util::MemorySegmentMapped& segment, bool create,
+                       std::string& error_msg);
+
+    void openReadWrite(const std::string& filename, bool create);
+    void openReadOnly(const std::string& filename);
+
+private:
+    isc::dns::RRClass rrclass_;
+    MemorySegmentOpenMode current_mode_;
+    std::string current_filename_;
+    // Internally holds a MemorySegmentMapped. This is NULL on
+    // construction, and is set by the \c reset() method.
+    boost::scoped_ptr<isc::util::MemorySegmentMapped> mem_sgmt_;
+    ZoneTableHeader* header_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // ZONE_TABLE_SEGMENT_MAPPED_H

+ 22 - 19
src/lib/datasrc/memory/zone_writer_local.cc

@@ -12,9 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include "zone_writer_local.h"
-#include "zone_data.h"
-#include "zone_table_segment_local.h"
+#include <datasrc/memory/zone_writer.h>
+#include <datasrc/memory/zone_data.h>
 
 
 #include <memory>
 #include <memory>
 
 
@@ -24,33 +23,38 @@ namespace isc {
 namespace datasrc {
 namespace datasrc {
 namespace memory {
 namespace memory {
 
 
-ZoneWriterLocal::ZoneWriterLocal(ZoneTableSegmentLocal* segment,
-                                 const LoadAction& load_action,
-                                 const dns::Name& origin,
-                                 const dns::RRClass& rrclass) :
+ZoneWriter::ZoneWriter(ZoneTableSegment& segment,
+                       const LoadAction& load_action,
+                       const dns::Name& origin,
+                       const dns::RRClass& rrclass) :
     segment_(segment),
     segment_(segment),
     load_action_(load_action),
     load_action_(load_action),
     origin_(origin),
     origin_(origin),
     rrclass_(rrclass),
     rrclass_(rrclass),
     zone_data_(NULL),
     zone_data_(NULL),
     state_(ZW_UNUSED)
     state_(ZW_UNUSED)
-{}
+{
+    if (!segment.isWritable()) {
+        isc_throw(isc::InvalidOperation,
+                  "Attempt to construct ZoneWriter for a read-only segment");
+    }
+}
 
 
-ZoneWriterLocal::~ZoneWriterLocal() {
+ZoneWriter::~ZoneWriter() {
     // Clean up everything there might be left if someone forgot, just
     // Clean up everything there might be left if someone forgot, just
     // in case.
     // in case.
     cleanup();
     cleanup();
 }
 }
 
 
 void
 void
-ZoneWriterLocal::load() {
+ZoneWriter::load() {
     if (state_ != ZW_UNUSED) {
     if (state_ != ZW_UNUSED) {
         isc_throw(isc::InvalidOperation, "Trying to load twice");
         isc_throw(isc::InvalidOperation, "Trying to load twice");
     }
     }
 
 
-    zone_data_ = load_action_(segment_->getMemorySegment());
+    zone_data_ = load_action_(segment_.getMemorySegment());
 
 
-    if (zone_data_ == NULL) {
+    if (!zone_data_) {
         // Bug inside load_action_.
         // Bug inside load_action_.
         isc_throw(isc::InvalidOperation, "No data returned from load action");
         isc_throw(isc::InvalidOperation, "No data returned from load action");
     }
     }
@@ -59,18 +63,17 @@ ZoneWriterLocal::load() {
 }
 }
 
 
 void
 void
-ZoneWriterLocal::install() {
+ZoneWriter::install() {
     if (state_ != ZW_LOADED) {
     if (state_ != ZW_LOADED) {
         isc_throw(isc::InvalidOperation, "No data to install");
         isc_throw(isc::InvalidOperation, "No data to install");
     }
     }
 
 
-
-    ZoneTable* table(segment_->getHeader().getTable());
-    if (table == NULL) {
+    ZoneTable* table(segment_.getHeader().getTable());
+    if (!table) {
         isc_throw(isc::Unexpected, "No zone table present");
         isc_throw(isc::Unexpected, "No zone table present");
     }
     }
     const ZoneTable::AddResult result(table->addZone(
     const ZoneTable::AddResult result(table->addZone(
-                                          segment_->getMemorySegment(),
+                                          segment_.getMemorySegment(),
                                           rrclass_, origin_, zone_data_));
                                           rrclass_, origin_, zone_data_));
 
 
     state_ = ZW_INSTALLED;
     state_ = ZW_INSTALLED;
@@ -78,11 +81,11 @@ ZoneWriterLocal::install() {
 }
 }
 
 
 void
 void
-ZoneWriterLocal::cleanup() {
+ZoneWriter::cleanup() {
     // We eat the data (if any) now.
     // We eat the data (if any) now.
 
 
     if (zone_data_ != NULL) {
     if (zone_data_ != NULL) {
-        ZoneData::destroy(segment_->getMemorySegment(), zone_data_, rrclass_);
+        ZoneData::destroy(segment_.getMemorySegment(), zone_data_, rrclass_);
         zone_data_ = NULL;
         zone_data_ = NULL;
         state_ = ZW_CLEANED;
         state_ = ZW_CLEANED;
     }
     }

+ 45 - 16
src/lib/datasrc/memory/zone_writer.h

@@ -15,7 +15,11 @@
 #ifndef MEM_ZONE_WRITER_H
 #ifndef MEM_ZONE_WRITER_H
 #define MEM_ZONE_WRITER_H
 #define MEM_ZONE_WRITER_H
 
 
-#include "load_action.h"
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/load_action.h>
+
+#include <dns/rrclass.h>
+#include <dns/name.h>
 
 
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
@@ -23,22 +27,33 @@ namespace memory {
 
 
 /// \brief Does an update to a zone.
 /// \brief Does an update to a zone.
 ///
 ///
-/// This abstract base class represents the work of a reload of a zone.
-/// The work is divided into three stages -- load(), install() and cleanup().
-/// They should be called in this order for the effect to take place.
+/// This represents the work of a (re)load of a zone.  The work is divided
+/// into three stages -- load(), install() and cleanup().  They should
+/// be called in this order for the effect to take place.
 ///
 ///
 /// We divide them so the update of zone data can be done asynchronously,
 /// We divide them so the update of zone data can be done asynchronously,
 /// in a different thread. The install() operation is the only one that needs
 /// in a different thread. The install() operation is the only one that needs
 /// to be done in a critical section.
 /// to be done in a critical section.
 ///
 ///
-/// Each derived class implementation must provide the strong exception
-/// guarantee for each public method. That is, when any of the methods
-/// throws, the entire state should stay the same as before the call
-/// (how to achieve that may be implementation dependant).
+/// This class provides strong exception guarantee for each public
+/// method. That is, when any of the methods throws, the entire state
+/// stays the same as before the call.
 class ZoneWriter {
 class ZoneWriter {
 public:
 public:
-    /// \brief Virtual destructor.
-    virtual ~ZoneWriter() {};
+    /// \brief Constructor
+    ///
+    /// \throw isc::InvalidOperation if \c segment is read-only.
+    ///
+    /// \param segment The zone table segment to store the zone into.
+    /// \param load_action The callback used to load data.
+    /// \param name The name of the zone.
+    /// \param rrclass The class of the zone.
+    ZoneWriter(ZoneTableSegment& segment,
+               const LoadAction& load_action, const dns::Name& name,
+               const dns::RRClass& rrclass);
+
+    /// \brief Destructor.
+    ~ZoneWriter();
 
 
     /// \brief Get the zone data into memory.
     /// \brief Get the zone data into memory.
     ///
     ///
@@ -56,7 +71,7 @@ public:
     /// \note After successful load(), you have to call cleanup() some time
     /// \note After successful load(), you have to call cleanup() some time
     ///     later.
     ///     later.
     /// \throw isc::InvalidOperation if called second time.
     /// \throw isc::InvalidOperation if called second time.
-    virtual void load() = 0;
+    void load();
 
 
     /// \brief Put the changes to effect.
     /// \brief Put the changes to effect.
     ///
     ///
@@ -68,12 +83,12 @@ public:
     /// The operation is expected to be fast and is meant to be used inside
     /// The operation is expected to be fast and is meant to be used inside
     /// a critical section.
     /// a critical section.
     ///
     ///
-    /// This may throw in rare cases, depending on the concrete implementation.
-    /// If it throws, you still need to call cleanup().
+    /// This may throw in rare cases.  If it throws, you still need to
+    /// call cleanup().
     ///
     ///
     /// \throw isc::InvalidOperation if called without previous load() or for
     /// \throw isc::InvalidOperation if called without previous load() or for
     ///     the second time or cleanup() was called already.
     ///     the second time or cleanup() was called already.
-    virtual void install() = 0;
+    void install();
 
 
     /// \brief Clean up resources.
     /// \brief Clean up resources.
     ///
     ///
@@ -81,8 +96,22 @@ public:
     /// one loaded by load() in case install() was not called or was not
     /// one loaded by load() in case install() was not called or was not
     /// successful, or the one replaced in install().
     /// successful, or the one replaced in install().
     ///
     ///
-    /// Generally, this should never throw.
-    virtual void cleanup() = 0;
+    /// \throw none
+    void cleanup();
+
+private:
+    ZoneTableSegment& segment_;
+    const LoadAction load_action_;
+    const dns::Name origin_;
+    const dns::RRClass rrclass_;
+    ZoneData* zone_data_;
+    enum State {
+        ZW_UNUSED,
+        ZW_LOADED,
+        ZW_INSTALLED,
+        ZW_CLEANED
+    };
+    State state_;
 };
 };
 
 
 }
 }

+ 0 - 95
src/lib/datasrc/memory/zone_writer_local.h

@@ -1,95 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef MEM_ZONE_WRITER_LOCAL_H
-#define MEM_ZONE_WRITER_LOCAL_H
-
-#include "zone_writer.h"
-
-#include <dns/rrclass.h>
-#include <dns/name.h>
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-class ZoneData;
-class ZoneTableSegmentLocal;
-
-/// \brief Writer implementation which loads data locally.
-///
-/// This implementation prepares a clean zone data and lets one callback
-/// to fill it and another to install it somewhere. The class does mostly
-/// nothing (and delegates the work to the callbacks), just stores little bit
-/// of state between the calls.
-class ZoneWriterLocal : public ZoneWriter {
-public:
-    /// \brief Constructor
-    ///
-    /// \param segment The zone table segment to store the zone into.
-    /// \param load_action The callback used to load data.
-    /// \param install_action The callback used to install the loaded zone.
-    /// \param rrclass The class of the zone.
-    ZoneWriterLocal(ZoneTableSegmentLocal* segment,
-                    const LoadAction& load_action, const dns::Name& name,
-                    const dns::RRClass& rrclass);
-
-    /// \brief Destructor
-    ~ZoneWriterLocal();
-
-    /// \brief Loads the data.
-    ///
-    /// This calls the load_action (passed to constructor) and stores the
-    /// data for future use.
-    ///
-    /// \throw isc::InvalidOperation if it is called the second time in
-    ///     lifetime of the object.
-    /// \throw Whatever the load_action throws, it is propagated up.
-    virtual void load();
-
-    /// \brief Installs the zone.
-    ///
-    /// It modifies the zone table accessible through the segment (passed to
-    /// constructor).
-    ///
-    /// \throw isc::InvalidOperation if it is called the second time in
-    ///     lifetime of the object or if load() was not called previously or if
-    ///     cleanup() was already called.
-    virtual void install();
-
-    /// \brief Clean up memory.
-    ///
-    /// Cleans up the memory used by load()ed zone if not yet installed, or
-    /// the old zone replaced by install().
-    virtual void cleanup();
-private:
-    ZoneTableSegmentLocal* segment_;
-    LoadAction load_action_;
-    dns::Name origin_;
-    dns::RRClass rrclass_;
-    ZoneData* zone_data_;
-    enum State {
-        ZW_UNUSED,
-        ZW_LOADED,
-        ZW_INSTALLED,
-        ZW_CLEANED
-    };
-    State state_;
-};
-
-}
-}
-}
-
-#endif

+ 2 - 1
src/lib/datasrc/tests/client_list_unittest.cc

@@ -166,7 +166,8 @@ public:
         // Load the data into the zone table.
         // Load the data into the zone table.
         if (enabled) {
         if (enabled) {
             boost::scoped_ptr<memory::ZoneWriter> writer(
             boost::scoped_ptr<memory::ZoneWriter> writer(
-                dsrc_info.ztable_segment_->getZoneWriter(
+                new memory::ZoneWriter(
+                    *dsrc_info.ztable_segment_,
                     cache_conf->getLoadAction(rrclass_, zone),
                     cache_conf->getLoadAction(rrclass_, zone),
                     zone, rrclass_));
                     zone, rrclass_));
             writer->load();
             writer->load();

+ 6 - 0
src/lib/datasrc/tests/memory/Makefile.am

@@ -4,6 +4,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_builddir)\"
 
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 
@@ -38,6 +39,11 @@ run_unittests_SOURCES += zone_data_loader_unittest.cc
 run_unittests_SOURCES += zone_data_updater_unittest.cc
 run_unittests_SOURCES += zone_data_updater_unittest.cc
 run_unittests_SOURCES += zone_table_segment_test.h
 run_unittests_SOURCES += zone_table_segment_test.h
 run_unittests_SOURCES += zone_table_segment_unittest.cc
 run_unittests_SOURCES += zone_table_segment_unittest.cc
+
+if USE_SHARED_MEMORY
+run_unittests_SOURCES += zone_table_segment_mapped_unittest.cc
+endif
+
 run_unittests_SOURCES += zone_writer_unittest.cc
 run_unittests_SOURCES += zone_writer_unittest.cc
 
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

+ 6 - 4
src/lib/datasrc/tests/memory/zone_loader_util.cc

@@ -44,8 +44,9 @@ loadZoneIntoTable(ZoneTableSegment& zt_sgmt, const dns::Name& zname,
             " \"params\": {\"" + zname.toText() + "\": \"" + zone_file +
             " \"params\": {\"" + zname.toText() + "\": \"" + zone_file +
             "\"}}"), true);
             "\"}}"), true);
     boost::scoped_ptr<memory::ZoneWriter> writer(
     boost::scoped_ptr<memory::ZoneWriter> writer(
-        zt_sgmt.getZoneWriter(cache_conf.getLoadAction(zclass, zname),
-                              zname, zclass));
+        new memory::ZoneWriter(zt_sgmt,
+                               cache_conf.getLoadAction(zclass, zname),
+                               zname, zclass));
     writer->load();
     writer->load();
     writer->install();
     writer->install();
     writer->cleanup();
     writer->cleanup();
@@ -76,8 +77,9 @@ loadZoneIntoTable(ZoneTableSegment& zt_sgmt, const dns::Name& zname,
                   const dns::RRClass& zclass, ZoneIterator& iterator)
                   const dns::RRClass& zclass, ZoneIterator& iterator)
 {
 {
     boost::scoped_ptr<memory::ZoneWriter> writer(
     boost::scoped_ptr<memory::ZoneWriter> writer(
-        zt_sgmt.getZoneWriter(IteratorLoader(zclass, zname, iterator),
-                              zname, zclass));
+        new memory::ZoneWriter(zt_sgmt,
+                               IteratorLoader(zclass, zname, iterator),
+                               zname, zclass));
     writer->load();
     writer->load();
     writer->install();
     writer->install();
     writer->cleanup();
     writer->cleanup();

+ 192 - 0
src/lib/datasrc/tests/memory/zone_table_segment_mapped_unittest.cc

@@ -0,0 +1,192 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_writer.h>
+#include <datasrc/memory/zone_table_segment_mapped.h>
+
+#include <gtest/gtest.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/interprocess/file_mapping.hpp>
+
+using namespace isc::dns;
+using namespace isc::datasrc::memory;
+using namespace isc::data;
+using namespace isc::util;
+using namespace std;
+using boost::scoped_ptr;
+
+namespace {
+
+const char* mapped_file = TEST_DATA_BUILDDIR "/test.mapped";
+
+class ZoneTableSegmentMappedTest : public ::testing::Test {
+protected:
+    ZoneTableSegmentMappedTest() :
+        ztable_segment_(
+            ZoneTableSegment::create(RRClass::IN(), "mapped")),
+        config_params_(
+            Element::fromJSON(
+                "{\"mapped-file\": \"" + std::string(mapped_file) + "\"}"))
+    {
+        EXPECT_NE(static_cast<void*>(NULL), ztable_segment_);
+        // Verify that a ZoneTableSegmentMapped is created.
+        ZoneTableSegmentMapped* mapped_segment =
+            dynamic_cast<ZoneTableSegmentMapped*>(ztable_segment_);
+        EXPECT_NE(static_cast<void*>(NULL), mapped_segment);
+    }
+
+    ~ZoneTableSegmentMappedTest() {
+        ZoneTableSegment::destroy(ztable_segment_);
+        boost::interprocess::file_mapping::remove(mapped_file);
+    }
+
+    ZoneTableSegment* ztable_segment_;
+    const ConstElementPtr config_params_;
+};
+
+TEST_F(ZoneTableSegmentMappedTest, getHeaderUninitialized) {
+    // This should throw as we haven't called reset() yet.
+    EXPECT_THROW(ztable_segment_->getHeader(), isc::InvalidOperation);
+}
+
+TEST_F(ZoneTableSegmentMappedTest, getMemorySegmentUninitialized) {
+    // This should throw as we haven't called reset() yet.
+    EXPECT_THROW(ztable_segment_->getMemorySegment(), isc::InvalidOperation);
+}
+
+TEST_F(ZoneTableSegmentMappedTest, isWritableUninitialized) {
+    // isWritable() must return false by default, when the segment has
+    // not been reset() yet.
+    EXPECT_FALSE(ztable_segment_->isWritable());
+}
+
+TEST_F(ZoneTableSegmentMappedTest, resetBadConfig) {
+    // NULL is passed in config params
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               ConstElementPtr());
+    }, isc::InvalidParameter);
+
+    // Not a map
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("42"));
+    }, isc::InvalidParameter);
+
+    // Empty map
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("{}"));
+    }, isc::InvalidParameter);
+
+    // No "mapped-file" key
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("{\"foo\": \"bar\"}"));
+    }, isc::InvalidParameter);
+
+    // Value of "mapped-file" key is not a string
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("{\"mapped-file\": 42}"));
+    }, isc::InvalidParameter);
+
+    // The following should still throw, unaffected by the failed opens.
+    EXPECT_THROW(ztable_segment_->getHeader(), isc::InvalidOperation);
+    EXPECT_THROW(ztable_segment_->getMemorySegment(), isc::InvalidOperation);
+
+    // isWritable() must still return false, because the segment has not
+    // been successfully reset() yet.
+    EXPECT_FALSE(ztable_segment_->isWritable());
+}
+
+TEST_F(ZoneTableSegmentMappedTest, reset) {
+    // By default, the mapped file doesn't exist, so we cannot open it
+    // in READ_ONLY mode (which does not create the file).
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::READ_ONLY,
+                               config_params_);
+    }, MemorySegmentOpenError);
+
+    // The following should still throw, unaffected by the failed open.
+    EXPECT_THROW(ztable_segment_->getHeader(), isc::InvalidOperation);
+    EXPECT_THROW(ztable_segment_->getMemorySegment(), isc::InvalidOperation);
+
+    // isWritable() must still return false, because the segment has not
+    // been successfully reset() yet.
+    EXPECT_FALSE(ztable_segment_->isWritable());
+
+    // READ_WRITE mode must create the mapped file if it doesn't exist
+    // (and must not result in an exception).
+    ztable_segment_->reset(ZoneTableSegment::READ_WRITE,
+                           config_params_);
+    // This must not throw now.
+    EXPECT_TRUE(ztable_segment_->isWritable());
+
+    // The following method calls should no longer throw:
+    EXPECT_NO_THROW(ztable_segment_->getHeader());
+    EXPECT_NO_THROW(ztable_segment_->getMemorySegment());
+
+    // Let's try to re-open the mapped file in READ_ONLY mode. It should
+    // not fail now.
+    ztable_segment_->reset(ZoneTableSegment::READ_ONLY,
+                           config_params_);
+    EXPECT_FALSE(ztable_segment_->isWritable());
+
+    // Re-creating the mapped file should erase old data and should not
+    // trigger any exceptions inside reset() due to old data (such as
+    // named addresses).
+    ztable_segment_->reset(ZoneTableSegment::CREATE,
+                           config_params_);
+    EXPECT_TRUE(ztable_segment_->isWritable());
+
+    // When we reset() with an invalid paramter and it fails, then the
+    // segment should still be usable.
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("{}"));
+    }, isc::InvalidParameter);
+    EXPECT_TRUE(ztable_segment_->isWritable());
+    // The following should not throw.
+    EXPECT_NO_THROW(ztable_segment_->getHeader());
+    EXPECT_NO_THROW(ztable_segment_->getMemorySegment());
+
+    // READ_WRITE with an existing map file ought to work too. This
+    // would use existing named addresses. This actually re-opens the
+    // currently open map.
+    ztable_segment_->reset(ZoneTableSegment::READ_WRITE,
+                           config_params_);
+    EXPECT_TRUE(ztable_segment_->isWritable());
+}
+
+TEST_F(ZoneTableSegmentMappedTest, clear) {
+    // First, load an underlying mapped file
+    ztable_segment_->reset(ZoneTableSegment::READ_WRITE,
+                           config_params_);
+
+    EXPECT_TRUE(ztable_segment_->isWritable());
+    // The following method calls should no longer throw:
+    EXPECT_NO_THROW(ztable_segment_->getHeader());
+    EXPECT_NO_THROW(ztable_segment_->getMemorySegment());
+
+    // Now, clear the segment.
+    ztable_segment_->clear();
+
+    EXPECT_FALSE(ztable_segment_->isWritable());
+    // The following method calls should now throw.
+    EXPECT_THROW(ztable_segment_->getHeader(), isc::InvalidOperation);
+    EXPECT_THROW(ztable_segment_->getMemorySegment(), isc::InvalidOperation);
+}
+
+} // anonymous namespace

+ 12 - 46
src/lib/datasrc/tests/memory/zone_table_segment_test.h

@@ -18,7 +18,7 @@
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/zone_data.h>
 #include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/zone_writer_local.h>
+#include <datasrc/memory/zone_writer.h>
 
 
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
@@ -30,7 +30,7 @@ namespace test {
 // was de-allocated on it.
 // was de-allocated on it.
 class ZoneTableSegmentTest : public ZoneTableSegment {
 class ZoneTableSegmentTest : public ZoneTableSegment {
 public:
 public:
-    ZoneTableSegmentTest(isc::dns::RRClass rrclass,
+    ZoneTableSegmentTest(const isc::dns::RRClass& rrclass,
                          isc::util::MemorySegment& mem_sgmt) :
                          isc::util::MemorySegment& mem_sgmt) :
         ZoneTableSegment(rrclass),
         ZoneTableSegment(rrclass),
         mem_sgmt_(mem_sgmt),
         mem_sgmt_(mem_sgmt),
@@ -41,6 +41,14 @@ public:
         ZoneTable::destroy(mem_sgmt_, header_.getTable());
         ZoneTable::destroy(mem_sgmt_, header_.getTable());
     }
     }
 
 
+    virtual void reset(MemorySegmentOpenMode, isc::data::ConstElementPtr) {
+        isc_throw(isc::NotImplemented, "reset() is not implemented");
+    }
+
+    virtual void clear() {
+        isc_throw(isc::NotImplemented, "clear() is not implemented");
+    }
+
     virtual ZoneTableHeader& getHeader() {
     virtual ZoneTableHeader& getHeader() {
         return (header_);
         return (header_);
     }
     }
@@ -53,55 +61,13 @@ public:
         return (mem_sgmt_);
         return (mem_sgmt_);
     }
     }
 
 
-    virtual ZoneWriter* getZoneWriter(const LoadAction& load_action,
-                                      const dns::Name& name,
-                                      const dns::RRClass& rrclass)
-    {
-        return (new Writer(this, load_action, name, rrclass));
+    virtual bool isWritable() const {
+        return (true);
     }
     }
 
 
 private:
 private:
     isc::util::MemorySegment& mem_sgmt_;
     isc::util::MemorySegment& mem_sgmt_;
     ZoneTableHeader header_;
     ZoneTableHeader header_;
-
-    // A writer for this segment. The implementation is similar
-    // to ZoneWriterLocal, but all the error handling is stripped
-    // for simplicity. Also, we do everything inside the
-    // install(), for the same reason. We just need something
-    // inside the tests, not a full-blown implementation
-    // for background loading.
-    class Writer : public ZoneWriter {
-    public:
-        Writer(ZoneTableSegmentTest* segment, const LoadAction& load_action,
-               const dns::Name& name, const dns::RRClass& rrclass) :
-            segment_(segment),
-            load_action_(load_action),
-            name_(name),
-            rrclass_(rrclass)
-        {}
-
-        void load() {}
-
-        void install() {
-            ZoneTable* table(segment_->getHeader().getTable());
-            const ZoneTable::AddResult
-                result(table->addZone(segment_->getMemorySegment(), rrclass_,
-                                      name_,
-                                      load_action_(segment_->
-                                                   getMemorySegment())));
-            if (result.zone_data != NULL) {
-                ZoneData::destroy(segment_->getMemorySegment(),
-                                  result.zone_data, rrclass_);
-            }
-        }
-
-        virtual void cleanup() {}
-    private:
-        ZoneTableSegmentTest* segment_;
-        LoadAction load_action_;
-        dns::Name name_;
-        dns::RRClass rrclass_;
-    };
 };
 };
 
 
 } // namespace test
 } // namespace test

+ 19 - 18
src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc

@@ -12,9 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include <datasrc/memory/zone_writer_local.h>
+#include <datasrc/memory/zone_writer.h>
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_table_segment_local.h>
-#include <util/memory_segment_local.h>
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -52,6 +51,21 @@ TEST_F(ZoneTableSegmentTest, create) {
                  UnknownSegmentType);
                  UnknownSegmentType);
 }
 }
 
 
+TEST_F(ZoneTableSegmentTest, reset) {
+    // reset() should throw that it's not implemented so that any
+    // accidental calls are found out.
+    EXPECT_THROW({
+        ztable_segment_->reset(ZoneTableSegment::CREATE,
+                               Element::fromJSON("{}"));
+    }, isc::NotImplemented);
+}
+
+TEST_F(ZoneTableSegmentTest, clear) {
+    // clear() should throw that it's not implemented so that any
+    // accidental calls are found out.
+    EXPECT_THROW(ztable_segment_->clear(), isc::NotImplemented);
+}
+
 // Helper function to check const and non-const methods.
 // Helper function to check const and non-const methods.
 template <typename TS, typename TH, typename TT>
 template <typename TS, typename TH, typename TT>
 void
 void
@@ -79,22 +93,9 @@ TEST_F(ZoneTableSegmentTest, getMemorySegment) {
     mem_sgmt.allMemoryDeallocated(); // use mem_sgmt
     mem_sgmt.allMemoryDeallocated(); // use mem_sgmt
 }
 }
 
 
-ZoneData*
-loadAction(MemorySegment&) {
-    // The function won't be called, so this is OK
-    return (NULL);
-}
-
-// Test we can get a writer.
-TEST_F(ZoneTableSegmentTest, getZoneWriter) {
-    scoped_ptr<ZoneWriter>
-        writer(ztable_segment_->getZoneWriter(loadAction, Name("example.org"),
-                                              RRClass::IN()));
-    // We have to get something
-    EXPECT_NE(static_cast<void*>(NULL), writer.get());
-    // And for now, it should be the local writer
-    EXPECT_NE(static_cast<void*>(NULL),
-              dynamic_cast<ZoneWriterLocal*>(writer.get()));
+TEST_F(ZoneTableSegmentTest, isWritable) {
+    // Local segments are always writable.
+    EXPECT_TRUE(ztable_segment_->isWritable());
 }
 }
 
 
 } // anonymous namespace
 } // anonymous namespace

+ 44 - 18
src/lib/datasrc/tests/memory/zone_writer_unittest.cc

@@ -12,13 +12,16 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include <datasrc/memory/zone_writer_local.h>
+#include <datasrc/memory/zone_writer.h>
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_data.h>
 #include <datasrc/memory/zone_data.h>
 
 
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 #include <dns/name.h>
 #include <dns/name.h>
 
 
+#include <datasrc/tests/memory/memory_segment_test.h>
+#include <datasrc/tests/memory/zone_table_segment_test.h>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -29,20 +32,20 @@ using boost::bind;
 using isc::dns::RRClass;
 using isc::dns::RRClass;
 using isc::dns::Name;
 using isc::dns::Name;
 using namespace isc::datasrc::memory;
 using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::test;
 
 
 namespace {
 namespace {
 
 
 class TestException {};
 class TestException {};
 
 
-class ZoneWriterLocalTest : public ::testing::Test {
+class ZoneWriterTest : public ::testing::Test {
 public:
 public:
-    ZoneWriterLocalTest() :
+    ZoneWriterTest() :
         segment_(ZoneTableSegment::create(RRClass::IN(), "local")),
         segment_(ZoneTableSegment::create(RRClass::IN(), "local")),
         writer_(new
         writer_(new
-            ZoneWriterLocal(dynamic_cast<ZoneTableSegmentLocal*>(segment_.
-                                                                 get()),
-                            bind(&ZoneWriterLocalTest::loadAction, this, _1),
-                            Name("example.org"), RRClass::IN())),
+            ZoneWriter(*segment_,
+                       bind(&ZoneWriterTest::loadAction, this, _1),
+                       Name("example.org"), RRClass::IN())),
         load_called_(false),
         load_called_(false),
         load_throw_(false),
         load_throw_(false),
         load_null_(false),
         load_null_(false),
@@ -54,12 +57,12 @@ public:
     }
     }
 protected:
 protected:
     scoped_ptr<ZoneTableSegment> segment_;
     scoped_ptr<ZoneTableSegment> segment_;
-    scoped_ptr<ZoneWriterLocal> writer_;
+    scoped_ptr<ZoneWriter> writer_;
     bool load_called_;
     bool load_called_;
     bool load_throw_;
     bool load_throw_;
     bool load_null_;
     bool load_null_;
     bool load_data_;
     bool load_data_;
-private:
+public:
     ZoneData* loadAction(isc::util::MemorySegment& segment) {
     ZoneData* loadAction(isc::util::MemorySegment& segment) {
         // Make sure it is the correct segment passed. We know the
         // Make sure it is the correct segment passed. We know the
         // exact instance, can compare pointers to them.
         // exact instance, can compare pointers to them.
@@ -86,9 +89,32 @@ private:
     }
     }
 };
 };
 
 
+class ReadOnlySegment : public ZoneTableSegmentTest {
+public:
+    ReadOnlySegment(const isc::dns::RRClass& rrclass,
+                    isc::util::MemorySegment& mem_sgmt) :
+        ZoneTableSegmentTest(rrclass, mem_sgmt)
+    {}
+
+    // Returns false indicating it is a read-only segment. It is used in
+    // the ZoneWriter tests.
+    virtual bool isWritable() const {
+        return (false);
+    }
+};
+
+TEST_F(ZoneWriterTest, constructForReadOnlySegment) {
+    MemorySegmentTest mem_sgmt;
+    ReadOnlySegment ztable_segment(RRClass::IN(), mem_sgmt);
+    EXPECT_THROW(ZoneWriter(ztable_segment,
+                            bind(&ZoneWriterTest::loadAction, this, _1),
+                            Name("example.org"), RRClass::IN()),
+                 isc::InvalidOperation);
+}
+
 // We call it the way we are supposed to, check every callback is called in the
 // We call it the way we are supposed to, check every callback is called in the
 // right moment.
 // right moment.
-TEST_F(ZoneWriterLocalTest, correctCall) {
+TEST_F(ZoneWriterTest, correctCall) {
     // Nothing called before we call it
     // Nothing called before we call it
     EXPECT_FALSE(load_called_);
     EXPECT_FALSE(load_called_);
 
 
@@ -105,7 +131,7 @@ TEST_F(ZoneWriterLocalTest, correctCall) {
     EXPECT_NO_THROW(writer_->cleanup());
     EXPECT_NO_THROW(writer_->cleanup());
 }
 }
 
 
-TEST_F(ZoneWriterLocalTest, loadTwice) {
+TEST_F(ZoneWriterTest, loadTwice) {
     // Load it the first time
     // Load it the first time
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->load());
     EXPECT_TRUE(load_called_);
     EXPECT_TRUE(load_called_);
@@ -126,7 +152,7 @@ TEST_F(ZoneWriterLocalTest, loadTwice) {
 
 
 // Try loading after call to install and call to cleanup. Both is
 // Try loading after call to install and call to cleanup. Both is
 // forbidden.
 // forbidden.
-TEST_F(ZoneWriterLocalTest, loadLater) {
+TEST_F(ZoneWriterTest, loadLater) {
     // Load first, so we can install
     // Load first, so we can install
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->install());
     EXPECT_NO_THROW(writer_->install());
@@ -144,7 +170,7 @@ TEST_F(ZoneWriterLocalTest, loadLater) {
 }
 }
 
 
 // Try calling install at various bad times
 // Try calling install at various bad times
-TEST_F(ZoneWriterLocalTest, invalidInstall) {
+TEST_F(ZoneWriterTest, invalidInstall) {
     // Nothing loaded yet
     // Nothing loaded yet
     EXPECT_THROW(writer_->install(), isc::InvalidOperation);
     EXPECT_THROW(writer_->install(), isc::InvalidOperation);
     EXPECT_FALSE(load_called_);
     EXPECT_FALSE(load_called_);
@@ -161,7 +187,7 @@ TEST_F(ZoneWriterLocalTest, invalidInstall) {
 // We check we can clean without installing first and nothing bad
 // We check we can clean without installing first and nothing bad
 // happens. We also misuse the testcase to check we can't install
 // happens. We also misuse the testcase to check we can't install
 // after cleanup.
 // after cleanup.
-TEST_F(ZoneWriterLocalTest, cleanWithoutInstall) {
+TEST_F(ZoneWriterTest, cleanWithoutInstall) {
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->cleanup());
     EXPECT_NO_THROW(writer_->cleanup());
 
 
@@ -172,7 +198,7 @@ TEST_F(ZoneWriterLocalTest, cleanWithoutInstall) {
 }
 }
 
 
 // Test the case when load callback throws
 // Test the case when load callback throws
-TEST_F(ZoneWriterLocalTest, loadThrows) {
+TEST_F(ZoneWriterTest, loadThrows) {
     load_throw_ = true;
     load_throw_ = true;
     EXPECT_THROW(writer_->load(), TestException);
     EXPECT_THROW(writer_->load(), TestException);
 
 
@@ -186,7 +212,7 @@ TEST_F(ZoneWriterLocalTest, loadThrows) {
 
 
 // Check the strong exception guarantee - if it throws, nothing happened
 // Check the strong exception guarantee - if it throws, nothing happened
 // to the content.
 // to the content.
-TEST_F(ZoneWriterLocalTest, retry) {
+TEST_F(ZoneWriterTest, retry) {
     // First attempt fails due to some exception.
     // First attempt fails due to some exception.
     load_throw_ = true;
     load_throw_ = true;
     EXPECT_THROW(writer_->load(), TestException);
     EXPECT_THROW(writer_->load(), TestException);
@@ -214,7 +240,7 @@ TEST_F(ZoneWriterLocalTest, retry) {
 }
 }
 
 
 // Check the writer defends itsefl when load action returns NULL
 // Check the writer defends itsefl when load action returns NULL
-TEST_F(ZoneWriterLocalTest, loadNull) {
+TEST_F(ZoneWriterTest, loadNull) {
     load_null_ = true;
     load_null_ = true;
     EXPECT_THROW(writer_->load(), isc::InvalidOperation);
     EXPECT_THROW(writer_->load(), isc::InvalidOperation);
 
 
@@ -226,7 +252,7 @@ TEST_F(ZoneWriterLocalTest, loadNull) {
 }
 }
 
 
 // Check the object cleans up in case we forget it.
 // Check the object cleans up in case we forget it.
-TEST_F(ZoneWriterLocalTest, autoCleanUp) {
+TEST_F(ZoneWriterTest, autoCleanUp) {
     // Load data and forget about it. It should get released
     // Load data and forget about it. It should get released
     // when the writer itself is destroyed.
     // when the writer itself is destroyed.
     EXPECT_NO_THROW(writer_->load());
     EXPECT_NO_THROW(writer_->load());

+ 3 - 2
src/lib/datasrc/tests/zone_finder_context_unittest.cc

@@ -80,8 +80,9 @@ createInMemoryClient(RRClass zclass, const Name& zname) {
     shared_ptr<ZoneTableSegment> ztable_segment(
     shared_ptr<ZoneTableSegment> ztable_segment(
         ZoneTableSegment::create(zclass, cache_conf.getSegmentType()));
         ZoneTableSegment::create(zclass, cache_conf.getSegmentType()));
     scoped_ptr<memory::ZoneWriter> writer(
     scoped_ptr<memory::ZoneWriter> writer(
-        ztable_segment->getZoneWriter(cache_conf.getLoadAction(zclass, zname),
-                                      zname, zclass));
+        new memory::ZoneWriter(*ztable_segment,
+                               cache_conf.getLoadAction(zclass, zname),
+                               zname, zclass));
     writer->load();
     writer->load();
     writer->install();
     writer->install();
     writer->cleanup();
     writer->cleanup();

+ 3 - 3
src/lib/datasrc/tests/zone_loader_unittest.cc

@@ -319,9 +319,9 @@ protected:
                                   rrclass_, cache_conf.getSegmentType()));
                                   rrclass_, cache_conf.getSegmentType()));
         if (filename) {
         if (filename) {
             boost::scoped_ptr<memory::ZoneWriter> writer(
             boost::scoped_ptr<memory::ZoneWriter> writer(
-                ztable_segment_->getZoneWriter(cache_conf.getLoadAction(
-                                                   rrclass_, zone),
-                                               zone, rrclass_));
+                new memory::ZoneWriter(*ztable_segment_,
+                                       cache_conf.getLoadAction(rrclass_, zone),
+                                       zone, rrclass_));
             writer->load();
             writer->load();
             writer->install();
             writer->install();
             writer->cleanup();
             writer->cleanup();

+ 12 - 4
src/lib/util/memory_segment.h

@@ -17,6 +17,8 @@
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <utility>
+
 #include <stdlib.h>
 #include <stdlib.h>
 
 
 namespace isc {
 namespace isc {
@@ -176,7 +178,8 @@ public:
     /// as \c addr even if it wouldn't be considered to "belong to" the
     /// as \c addr even if it wouldn't be considered to "belong to" the
     /// segment in its normal sense; it can be used to indicate that memory
     /// segment in its normal sense; it can be used to indicate that memory
     /// has not been allocated for the specified name.  A subsequent call
     /// has not been allocated for the specified name.  A subsequent call
-    /// to \c getNamedAddress() will return NULL for that name.
+    /// to \c getNamedAddress() will return std::pair (true, NULL) for
+    /// that name.
     ///
     ///
     /// \note Naming an address is intentionally separated from allocation
     /// \note Naming an address is intentionally separated from allocation
     /// so that, for example, one module of a program can name a memory
     /// so that, for example, one module of a program can name a memory
@@ -228,6 +231,9 @@ public:
         return (setNamedAddressImpl(name, addr));
         return (setNamedAddressImpl(name, addr));
     }
     }
 
 
+    /// \brief Type definition for result returned by getNamedAddress()
+    typedef std::pair<bool, void*> NamedAddressResult;
+
     /// \brief Return the address in the segment that has the given name.
     /// \brief Return the address in the segment that has the given name.
     ///
     ///
     /// This method returns the memory address in the segment corresponding
     /// This method returns the memory address in the segment corresponding
@@ -245,8 +251,10 @@ public:
     ///
     ///
     /// \param name A C string of which the segment memory address is to be
     /// \param name A C string of which the segment memory address is to be
     /// returned.  Must not be NULL.
     /// returned.  Must not be NULL.
-    /// \return The address associated with the name, or NULL if not found.
-    void* getNamedAddress(const char* name) {
+    /// \return An std::pair containing a bool (set to true if the name
+    /// was found, or false otherwise) and the address associated with
+    /// the name (which is undefined if the name was not found).
+    NamedAddressResult getNamedAddress(const char* name) {
         // This public method implements common validation.  The actual
         // This public method implements common validation.  The actual
         // work specific to the derived segment is delegated to the
         // work specific to the derived segment is delegated to the
         // corresponding protected method.
         // corresponding protected method.
@@ -288,7 +296,7 @@ protected:
     virtual bool setNamedAddressImpl(const char* name, void* addr) = 0;
     virtual bool setNamedAddressImpl(const char* name, void* addr) = 0;
 
 
     /// \brief Implementation of getNamedAddress beyond common validation.
     /// \brief Implementation of getNamedAddress beyond common validation.
-    virtual void* getNamedAddressImpl(const char* name) = 0;
+    virtual NamedAddressResult getNamedAddressImpl(const char* name) = 0;
 
 
     /// \brief Implementation of clearNamedAddress beyond common validation.
     /// \brief Implementation of clearNamedAddress beyond common validation.
     virtual bool clearNamedAddressImpl(const char* name) = 0;
     virtual bool clearNamedAddressImpl(const char* name) = 0;

+ 3 - 3
src/lib/util/memory_segment_local.cc

@@ -51,13 +51,13 @@ MemorySegmentLocal::allMemoryDeallocated() const {
     return (allocated_size_ == 0 && named_addrs_.empty());
     return (allocated_size_ == 0 && named_addrs_.empty());
 }
 }
 
 
-void*
+MemorySegment::NamedAddressResult
 MemorySegmentLocal::getNamedAddressImpl(const char* name) {
 MemorySegmentLocal::getNamedAddressImpl(const char* name) {
     std::map<std::string, void*>::iterator found = named_addrs_.find(name);
     std::map<std::string, void*>::iterator found = named_addrs_.find(name);
     if (found != named_addrs_.end()) {
     if (found != named_addrs_.end()) {
-        return (found->second);
+        return (NamedAddressResult(true, found->second));
     }
     }
-    return (0);
+    return (NamedAddressResult(false, NULL));
 }
 }
 
 
 bool
 bool

+ 1 - 1
src/lib/util/memory_segment_local.h

@@ -70,7 +70,7 @@ public:
     ///
     ///
     /// There's a small chance this method could throw std::bad_alloc.
     /// There's a small chance this method could throw std::bad_alloc.
     /// It should be considered a fatal error.
     /// It should be considered a fatal error.
-    virtual void* getNamedAddressImpl(const char* name);
+    virtual NamedAddressResult getNamedAddressImpl(const char* name);
 
 
     /// \brief Local segment version of setNamedAddress.
     /// \brief Local segment version of setNamedAddress.
     ///
     ///

+ 3 - 3
src/lib/util/memory_segment_mapped.cc

@@ -279,14 +279,14 @@ MemorySegmentMapped::allMemoryDeallocated() const {
     return (impl_->base_sgmt_->all_memory_deallocated());
     return (impl_->base_sgmt_->all_memory_deallocated());
 }
 }
 
 
-void*
+MemorySegment::NamedAddressResult
 MemorySegmentMapped::getNamedAddressImpl(const char* name) {
 MemorySegmentMapped::getNamedAddressImpl(const char* name) {
     offset_ptr<void>* storage =
     offset_ptr<void>* storage =
         impl_->base_sgmt_->find<offset_ptr<void> >(name).first;
         impl_->base_sgmt_->find<offset_ptr<void> >(name).first;
     if (storage) {
     if (storage) {
-        return (storage->get());
+        return (NamedAddressResult(true, storage->get()));
     }
     }
-    return (NULL);
+    return (NamedAddressResult(false, NULL));
 }
 }
 
 
 bool
 bool

+ 1 - 1
src/lib/util/memory_segment_mapped.h

@@ -195,7 +195,7 @@ public:
     /// \brief Mapped segment version of getNamedAddress.
     /// \brief Mapped segment version of getNamedAddress.
     ///
     ///
     /// This version never throws.
     /// This version never throws.
-    virtual void* getNamedAddressImpl(const char* name);
+    virtual NamedAddressResult getNamedAddressImpl(const char* name);
 
 
     /// \brief Mapped segment version of clearNamedAddress.
     /// \brief Mapped segment version of clearNamedAddress.
     ///
     ///

+ 8 - 8
src/lib/util/tests/memory_segment_common_unittest.cc

@@ -30,9 +30,8 @@ checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
     // NULL name is not allowed.
     // NULL name is not allowed.
     EXPECT_THROW(segment.getNamedAddress(NULL), InvalidParameter);
     EXPECT_THROW(segment.getNamedAddress(NULL), InvalidParameter);
 
 
-    // If the name does not exist, NULL should be returned.
-    EXPECT_EQ(static_cast<void*>(NULL),
-              segment.getNamedAddress("test address"));
+    // If the name does not exist, false should be returned.
+    EXPECT_FALSE(segment.getNamedAddress("test address").first);
 
 
     // Now set it
     // Now set it
     void* ptr32 = segment.allocate(sizeof(uint32_t));
     void* ptr32 = segment.allocate(sizeof(uint32_t));
@@ -44,7 +43,8 @@ checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
     EXPECT_THROW(segment.setNamedAddress(NULL, ptr32), InvalidParameter);
     EXPECT_THROW(segment.setNamedAddress(NULL, ptr32), InvalidParameter);
 
 
     // we can now get it; the stored value should be intact.
     // we can now get it; the stored value should be intact.
-    EXPECT_EQ(ptr32, segment.getNamedAddress("test address"));
+    EXPECT_EQ(MemorySegment::NamedAddressResult(true, ptr32),
+              segment.getNamedAddress("test address"));
     EXPECT_EQ(test_val, *static_cast<const uint32_t*>(ptr32));
     EXPECT_EQ(test_val, *static_cast<const uint32_t*>(ptr32));
 
 
     // Override it.
     // Override it.
@@ -52,20 +52,20 @@ checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
     const uint16_t test_val16 = 4200;
     const uint16_t test_val16 = 4200;
     *static_cast<uint16_t*>(ptr16) = test_val16;
     *static_cast<uint16_t*>(ptr16) = test_val16;
     EXPECT_FALSE(segment.setNamedAddress("test address", ptr16));
     EXPECT_FALSE(segment.setNamedAddress("test address", ptr16));
-    EXPECT_EQ(ptr16, segment.getNamedAddress("test address"));
+    EXPECT_EQ(MemorySegment::NamedAddressResult(true, ptr16),
+              segment.getNamedAddress("test address"));
     EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(ptr16));
     EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(ptr16));
 
 
     // Clear it.  Then we won't be able to find it any more.
     // Clear it.  Then we won't be able to find it any more.
     EXPECT_TRUE(segment.clearNamedAddress("test address"));
     EXPECT_TRUE(segment.clearNamedAddress("test address"));
-    EXPECT_EQ(static_cast<void*>(NULL),
-              segment.getNamedAddress("test address"));
+    EXPECT_FALSE(segment.getNamedAddress("test address").first);
 
 
     // duplicate attempt of clear will result in false as it doesn't exist.
     // duplicate attempt of clear will result in false as it doesn't exist.
     EXPECT_FALSE(segment.clearNamedAddress("test address"));
     EXPECT_FALSE(segment.clearNamedAddress("test address"));
 
 
     // Setting NULL is okay.
     // Setting NULL is okay.
     EXPECT_FALSE(segment.setNamedAddress("null address", NULL));
     EXPECT_FALSE(segment.setNamedAddress("null address", NULL));
-    EXPECT_EQ(static_cast<void*>(NULL),
+    EXPECT_EQ(MemorySegment::NamedAddressResult(true, NULL),
               segment.getNamedAddress("null address"));
               segment.getNamedAddress("null address"));
 
 
     // If the underlying implementation performs explicit check against
     // If the underlying implementation performs explicit check against

+ 32 - 22
src/lib/util/tests/memory_segment_mapped_unittest.cc

@@ -287,12 +287,14 @@ void
 checkNamedData(const std::string& name, const std::vector<uint8_t>& data,
 checkNamedData(const std::string& name, const std::vector<uint8_t>& data,
                MemorySegment& sgmt, bool delete_after_check = false)
                MemorySegment& sgmt, bool delete_after_check = false)
 {
 {
-    void* dp = sgmt.getNamedAddress(name.c_str());
-    ASSERT_TRUE(dp);
-    EXPECT_EQ(0, std::memcmp(dp, &data[0], data.size()));
+    const MemorySegment::NamedAddressResult result =
+        sgmt.getNamedAddress(name.c_str());
+    ASSERT_TRUE(result.first);
+    ASSERT_TRUE(result.second);
+    EXPECT_EQ(0, std::memcmp(result.second, &data[0], data.size()));
 
 
     if (delete_after_check) {
     if (delete_after_check) {
-        sgmt.deallocate(dp, data.size());
+        sgmt.deallocate(result.second, data.size());
         sgmt.clearNamedAddress(name.c_str());
         sgmt.clearNamedAddress(name.c_str());
     }
     }
 }
 }
@@ -309,10 +311,10 @@ TEST_F(MemorySegmentMappedTest, namedAddress) {
     segment_.reset();           // close it before opening another one
     segment_.reset();           // close it before opening another one
 
 
     segment_.reset(new MemorySegmentMapped(mapped_file));
     segment_.reset(new MemorySegmentMapped(mapped_file));
-    EXPECT_NE(static_cast<void*>(NULL),
-              segment_->getNamedAddress("test address"));
-    EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(
-                  segment_->getNamedAddress("test address")));
+    const MemorySegment::NamedAddressResult result =
+        segment_->getNamedAddress("test address");
+    ASSERT_TRUE(result.first);
+    EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(result.second));
 
 
     // try to set an unusually long name.  We re-create the file so
     // try to set an unusually long name.  We re-create the file so
     // creating the name would cause allocation failure and trigger internal
     // creating the name would cause allocation failure and trigger internal
@@ -323,7 +325,7 @@ TEST_F(MemorySegmentMappedTest, namedAddress) {
     const std::string long_name(1025, 'x'); // definitely larger than segment
     const std::string long_name(1025, 'x'); // definitely larger than segment
     // setNamedAddress should return true, indicating segment has grown.
     // setNamedAddress should return true, indicating segment has grown.
     EXPECT_TRUE(segment_->setNamedAddress(long_name.c_str(), NULL));
     EXPECT_TRUE(segment_->setNamedAddress(long_name.c_str(), NULL));
-    EXPECT_EQ(static_cast<void*>(NULL),
+    EXPECT_EQ(MemorySegment::NamedAddressResult(true, NULL),
               segment_->getNamedAddress(long_name.c_str()));
               segment_->getNamedAddress(long_name.c_str()));
 
 
     // Check contents pointed by named addresses survive growing and
     // Check contents pointed by named addresses survive growing and
@@ -410,10 +412,12 @@ TEST_F(MemorySegmentMappedTest, multiProcess) {
         EXPECT_EQ(0, from_parent);
         EXPECT_EQ(0, from_parent);
 
 
         MemorySegmentMapped sgmt(mapped_file);
         MemorySegmentMapped sgmt(mapped_file);
-        void* ptr_child = sgmt.getNamedAddress("test address");
-        EXPECT_TRUE(ptr_child);
-        if (ptr_child) {
-            const uint32_t val = *static_cast<const uint32_t*>(ptr_child);
+        const MemorySegment::NamedAddressResult result =
+            sgmt.getNamedAddress("test address");
+        ASSERT_TRUE(result.first);
+        EXPECT_TRUE(result.second);
+        if (result.second) {
+            const uint32_t val = *static_cast<const uint32_t*>(result.second);
             EXPECT_EQ(424242, val);
             EXPECT_EQ(424242, val);
             // tell the parent whether it succeeded. 0 means it did,
             // tell the parent whether it succeeded. 0 means it did,
             // 0xff means it failed.
             // 0xff means it failed.
@@ -425,9 +429,11 @@ TEST_F(MemorySegmentMappedTest, multiProcess) {
     // parent: open another read-only segment, then tell the child to open
     // parent: open another read-only segment, then tell the child to open
     // its own segment.
     // its own segment.
     segment_.reset(new MemorySegmentMapped(mapped_file));
     segment_.reset(new MemorySegmentMapped(mapped_file));
-    ptr = segment_->getNamedAddress("test address");
-    ASSERT_TRUE(ptr);
-    EXPECT_EQ(424242, *static_cast<const uint32_t*>(ptr));
+    const MemorySegment::NamedAddressResult result =
+        segment_->getNamedAddress("test address");
+    ASSERT_TRUE(result.first);
+    ASSERT_TRUE(result.second);
+    EXPECT_EQ(424242, *static_cast<const uint32_t*>(result.second));
     const char some_data = 0;
     const char some_data = 0;
     EXPECT_EQ(1, write(pipe_to_child.getWriteFD(), &some_data,
     EXPECT_EQ(1, write(pipe_to_child.getWriteFD(), &some_data,
                        sizeof(some_data)));
                        sizeof(some_data)));
@@ -477,9 +483,11 @@ TEST_F(MemorySegmentMappedTest, violateReadOnly) {
     if (!isc::util::unittests::runningOnValgrind()) {
     if (!isc::util::unittests::runningOnValgrind()) {
         EXPECT_DEATH_IF_SUPPORTED({
         EXPECT_DEATH_IF_SUPPORTED({
                 MemorySegmentMapped segment_ro(mapped_file);
                 MemorySegmentMapped segment_ro(mapped_file);
-                EXPECT_TRUE(segment_ro.getNamedAddress("test address"));
-                *static_cast<uint32_t*>(
-                    segment_ro.getNamedAddress("test address")) = 0;
+                const MemorySegment::NamedAddressResult result =
+                    segment_ro.getNamedAddress("test address");
+                ASSERT_TRUE(result.first);
+                ASSERT_TRUE(result.second);
+                *static_cast<uint32_t*>(result.second) = 0;
             }, "");
             }, "");
     }
     }
 
 
@@ -487,10 +495,12 @@ TEST_F(MemorySegmentMappedTest, violateReadOnly) {
     // attempts are prohibited. When detectable it must result in an
     // attempts are prohibited. When detectable it must result in an
     // exception.
     // exception.
     MemorySegmentMapped segment_ro(mapped_file);
     MemorySegmentMapped segment_ro(mapped_file);
-    ptr = segment_ro.getNamedAddress("test address");
-    EXPECT_NE(static_cast<void*>(NULL), ptr);
+    const MemorySegment::NamedAddressResult result =
+        segment_ro.getNamedAddress("test address");
+    ASSERT_TRUE(result.first);
+    EXPECT_NE(static_cast<void*>(NULL), result.second);
 
 
-    EXPECT_THROW(segment_ro.deallocate(ptr, 4), MemorySegmentError);
+    EXPECT_THROW(segment_ro.deallocate(result.second, 4), MemorySegmentError);
 
 
     EXPECT_THROW(segment_ro.allocate(16), MemorySegmentError);
     EXPECT_THROW(segment_ro.allocate(16), MemorySegmentError);
     // allocation that would otherwise require growing the segment; permission
     // allocation that would otherwise require growing the segment; permission