BIND 10 trac2831, updated. ba0d6139cadca933ebbaaa7f5016480154331ae6 [2831] make sure BOOST_NUMERIC_CAST_WOULDFAIL is set in all cases

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Apr 10 21:32:33 UTC 2013


The branch, trac2831 has been updated
       via  ba0d6139cadca933ebbaaa7f5016480154331ae6 (commit)
       via  9c36d2e22389ed7c9ae61fc91eb3f2946b10c169 (commit)
       via  00b7af245214cfdc54f522d719f23406d80d68ad (commit)
       via  d17b3c006757eecc1fab20cec90ead3e23b5962b (commit)
       via  08a77e0ef2096158eff12b5d20e9af9e46c93c45 (commit)
       via  e01ee176ce2071c076833c842a9cd946a5008130 (commit)
       via  be5ebe7673a8335d929803553bf3fce041a76bce (commit)
       via  bafbce54b6b042ff5fcf7bafc18b316c030f08c0 (commit)
       via  da33bc422e11f1cbf33427659a14291a32256833 (commit)
       via  2772477dcf13e7c524cfdaf4e39323f0716404dc (commit)
       via  faca56aea56b9eafd1093d06234650ea311a667d (commit)
       via  54fdd6d60c068faaac34d573acb095378672f47e (commit)
       via  0b18c4d0297e5e37ef0584b5e56912cc02715021 (commit)
       via  c52e7d7d0cfbdfbf4d7342f32024fdf4cae567c3 (commit)
       via  8599372b7fcf5a21c873d43189684b681b63307d (commit)
       via  ecc1ba7e65eaa8371cfdd8b83946c6a3bc2e5ee8 (commit)
       via  b38ed8f456895a7cb262b1b8cadadddc68964e6c (commit)
       via  5ab82e00091c504afeb54037a60671d344233512 (commit)
       via  3c93e56397f0af33064d449da82ca69d535a251a (commit)
       via  81e57da07f3b1a718fdd81991692e703f083219d (commit)
       via  b8d905c9dffd7f3973854d9f1bc7eac7086f4aa2 (commit)
       via  693068bdb12a36936d221f432e447afae6b05dc1 (commit)
       via  f6dae94a7b648f7fc766109c4f4c13152d45aa9b (commit)
       via  0f5e915ace1384308c9976c5f20dd49d5b2d05ab (commit)
       via  10d88f05a9705a4fb14afd6ceb89e976e3c195d0 (commit)
       via  617f1e6fc0da5415c244c45c211cae4f01b68585 (commit)
       via  8ddec7d1255e11ffaf69c7c827b42aeb69f95872 (commit)
      from  b215e58d58791c27f6116f65b66243b7a54ec14b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ba0d6139cadca933ebbaaa7f5016480154331ae6
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 14:31:07 2013 -0700

    [2831] make sure BOOST_NUMERIC_CAST_WOULDFAIL is set in all cases
    
    also, corrected incorrect reset of CXXFLAGS

commit 9c36d2e22389ed7c9ae61fc91eb3f2946b10c169
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 14:12:58 2013 -0700

    [2831] corrected a typo

commit 00b7af245214cfdc54f522d719f23406d80d68ad
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Mar 13 14:56:11 2013 -0700

    [2831] corrected the position of BOOST_MAPPED_FILE_CXXFLAG

commit d17b3c006757eecc1fab20cec90ead3e23b5962b
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Mar 13 21:55:30 2013 +0000

    [2831] set -Wall and -Wextra to detect possible warnings more accurately.

commit 08a77e0ef2096158eff12b5d20e9af9e46c93c45
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Mar 13 14:26:20 2013 -0700

    [2831] allow skipping build sh-mem support. set -Wno-error if necessary.

commit e01ee176ce2071c076833c842a9cd946a5008130
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Mar 13 14:24:31 2013 -0700

    [2831] detect buildability of managed_mapped_file, w/ or w/o -Werror.

commit be5ebe7673a8335d929803553bf3fce041a76bce
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 11:58:24 2013 -0700

    [2831] updated rw ctor so that we can create a new segment, removing old one.
    
    also clarified that in other read-write open existing file isn't modified.

commit bafbce54b6b042ff5fcf7bafc18b316c030f08c0
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 11:13:00 2013 -0700

    [2831] added note about too small initial_size, and added a test for the case.

commit da33bc422e11f1cbf33427659a14291a32256833
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 10:44:08 2013 -0700

    [2831] clarifed the intent of the assert() in growSegment().

commit 2772477dcf13e7c524cfdaf4e39323f0716404dc
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 10:38:00 2013 -0700

    [2831] simplified code a bit using static_cast instead of memcpy

commit faca56aea56b9eafd1093d06234650ea311a667d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 10:24:24 2013 -0700

    [2831] added a test to have another process open and read mapped memory

commit 54fdd6d60c068faaac34d573acb095378672f47e
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 10 10:03:16 2013 -0700

    [2831] extracted interproc synchronization utility from syncfile test
    
    so we can share it with memory-sement-mapped.

commit 0b18c4d0297e5e37ef0584b5e56912cc02715021
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 20:56:14 2013 -0700

    [2831] removed unnecessary flush before reset() (as the dtor calls flush)

commit c52e7d7d0cfbdfbf4d7342f32024fdf4cae567c3
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 20:34:29 2013 -0700

    [2831] check two different addressed from getNamedAddress can share the data.

commit 8599372b7fcf5a21c873d43189684b681b63307d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 20:15:48 2013 -0700

    [2831] added a test checking named data survive grow/shrink

commit ecc1ba7e65eaa8371cfdd8b83946c6a3bc2e5ee8
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 16:46:59 2013 -0700

    [2831] corrected example code in doc

commit b38ed8f456895a7cb262b1b8cadadddc68964e6c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 16:12:49 2013 -0700

    [2831] use \throw to describe exceptions where appropriate.
    
    also changed some of `InvalidOperation` cases to
    `MemorySegmentError` as it should be basically generic enough at the
    base class level.

commit 5ab82e00091c504afeb54037a60671d344233512
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 15:34:12 2013 -0700

    [2831] added more comments about crash in boost on multiple calls to shrink.

commit 3c93e56397f0af33064d449da82ca69d535a251a
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 15:21:02 2013 -0700

    [2831] corrected exception what() message.

commit 81e57da07f3b1a718fdd81991692e703f083219d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 15:18:06 2013 -0700

    [2831] handle NULL name values for xxxNamedAddress explicitly.

commit b8d905c9dffd7f3973854d9f1bc7eac7086f4aa2
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 14:52:53 2013 -0700

    [2831] described implementation-specific differences of setNamedAddress.

commit 693068bdb12a36936d221f432e447afae6b05dc1
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 14:46:18 2013 -0700

    [2831] define MemorySegmentMapped::INITIAL_SIZE explicitly as it's public.

commit f6dae94a7b648f7fc766109c4f4c13152d45aa9b
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 14:41:58 2013 -0700

    [2831] use NULL instead of 0; maybe worth discussing, but out of scope of task.

commit 0f5e915ace1384308c9976c5f20dd49d5b2d05ab
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 14:20:57 2013 -0700

    [2831] more explicitly clarify passing NULL to setNamedAddress.

commit 10d88f05a9705a4fb14afd6ceb89e976e3c195d0
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 14:15:35 2013 -0700

    [2831] explain the rational behind the difference between allocate/set name
    
    regarding the case where the segment has grown due to the allocation.

commit 617f1e6fc0da5415c244c45c211cae4f01b68585
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 13:55:03 2013 -0700

    [2831] explain the design rationale of MemorySegmentGrown

commit 8ddec7d1255e11ffaf69c7c827b42aeb69f95872
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 9 13:00:11 2013 -0700

    [2831] minor editorial fixes: corrected a typo; folded a long line.

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |   11 +
 m4macros/ax_boost_for_bind10.m4                    |   49 ++++-
 src/lib/util/Makefile.am                           |   14 ++
 src/lib/util/memory_segment.h                      |  121 ++++++++++-
 src/lib/util/memory_segment_local.cc               |    6 +-
 src/lib/util/memory_segment_local.h                |   11 +-
 src/lib/util/memory_segment_mapped.cc              |   83 ++++++--
 src/lib/util/memory_segment_mapped.h               |   71 +++++--
 src/lib/util/tests/Makefile.am                     |    3 +
 .../util/tests/interprocess_sync_file_unittest.cc  |   38 +---
 .../tests/interprocess_util.cc}                    |   43 ++--
 .../tests/interprocess_util.h}                     |   28 ++-
 .../util/tests/memory_segment_common_unittest.cc   |   23 +-
 .../util/tests/memory_segment_mapped_unittest.cc   |  221 +++++++++++++++++---
 14 files changed, 568 insertions(+), 154 deletions(-)
 copy src/lib/{dns/python/zone_checker_python.h => util/tests/interprocess_util.cc} (60%)
 copy src/lib/{dns/python/zone_checker_python.h => util/tests/interprocess_util.h} (67%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 956c387..066dcb2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -884,6 +884,17 @@ if test "$BOOST_NUMERIC_CAST_WOULDFAIL" = "yes" -a X"$werror_ok" = X1 -a $CLANGP
     AC_MSG_ERROR([Failed to compile a required header file.  If you are using FreeBSD and Boost installed via ports, retry with specifying --without-werror.  See the ChangeLog entry for Trac no. 1991 for more details.])
 fi
 
+use_shared_memory=yes
+AC_ARG_WITH(shared-memory,
+    AC_HELP_STRING([--with-shared-memory],
+    [Build with Boost shared memory support; for large scale authoritative DNS servers]),
+    [use_shared_memory=$withval])
+if test X$use_shared_memory = Xyes -a "$BOOST_MAPPED_FILE_WOULDFAIL" = "yes"; then
+    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; using a different compiler or a different version of Boost may also help.])
+fi
+AM_CONDITIONAL([USE_SHARED_MEMORY], [test x$use_shared_memory = xyes])
+AC_SUBST(BOOST_MAPPED_FILE_CXXFLAG)
+
 # Add some default CPP flags needed for Boost, identified by the AX macro.
 CPPFLAGS="$CPPFLAGS $CPPFLAGS_BOOST_THREADCONF"
 
diff --git a/m4macros/ax_boost_for_bind10.m4 b/m4macros/ax_boost_for_bind10.m4
index 1ce367e..577af6b 100644
--- a/m4macros/ax_boost_for_bind10.m4
+++ b/m4macros/ax_boost_for_bind10.m4
@@ -23,7 +23,11 @@ dnl   BOOST_OFFSET_PTR_WOULDFAIL set to "yes" if offset_ptr would cause build
 dnl                              error; otherwise set to "no"
 dnl   BOOST_NUMERIC_CAST_WOULDFAIL set to "yes" if numeric_cast would cause
 dnl                                build error; otherwise set to "no"
-dnl
+dnl   BOOST_MAPPED_FILE_WOULDFAIL set to "yes" if managed_mapped_file would
+dnl                               cause build failure; otherwise set to "no"
+dnl   BOOST_MAPPED_FILE_CXXFLAG set to the compiler flag that would need to
+dnl                             compile managed_mapped_file (can be empty).
+dnl                             It is of no use if "WOULDFAIL" is yes.
 
 AC_DEFUN([AX_BOOST_FOR_BIND10], [
 AC_LANG_SAVE
@@ -101,10 +105,49 @@ if test "X$GXX" = "Xyes"; then
 
    CXXFLAGS="$CXXFLAGS_SAVED"
 else
-  # This doesn't matter for non-g++
-  BOOST_NUMERIC_CAST_WOULDFAIL=no
+   # This doesn't matter for non-g++
+   BOOST_NUMERIC_CAST_WOULDFAIL=no
 fi
 
+# Boost interprocess::managed_mapped_file is highly system dependent and
+# can cause many portability issues.  We are going to check if it could
+# compile at all, possibly with being lenient about compiler warnings.
+BOOST_MAPPED_FILE_WOULDFAIL=yes
+BOOST_MAPPED_FILE_CXXFLAG=
+CXXFLAGS_SAVED="$CXXFLAGS"
+try_flags="no"
+if test "X$GXX" = "Xyes"; then
+  CXXFLAGS="$CXXFLAGS -Wall -Wextra -Werror"
+  try_flags="$try_flags -Wno-error"
+fi
+# clang can cause false positives with -Werror without -Qunused-arguments
+AC_CHECK_DECL([__clang__], [CXXFLAGS="$CXXFLAGS -Qunused-arguments"], [])
+
+AC_MSG_CHECKING([Boost managed_mapped_file compiles])
+CXXFLAGS_SAVED2="$CXXFLAGS"
+for flag in $try_flags; do
+  if test "$flag" != no; then
+    BOOST_MAPPED_FILE_CXXFLAG="$flag"
+  fi
+  CXXFLAGS="$CXXFLAGS $BOOST_MAPPED_FILE_CXXFLAG"
+  AC_TRY_COMPILE([
+  #include <boost/interprocess/managed_mapped_file.hpp>
+  ],[
+  return (boost::interprocess::managed_mapped_file().all_memory_deallocated());
+  ],[AC_MSG_RESULT([yes, with $flag flag])
+     BOOST_MAPPED_FILE_WOULDFAIL=no
+     break
+  ],[])
+
+  CXXFLAGS="$CXXFLAGS_SAVED2"
+done
+
+if test $BOOST_MAPPED_FILE_WOULDFAIL = yes; then
+  AC_MSG_RESULT(no)
+fi
+
+CXXFLAGS="$CXXFLAGS_SAVED"
+
 AC_SUBST(BOOST_INCLUDES)
 
 CPPFLAGS="$CPPFLAGS_SAVED"
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 1fd2de4..32a9341 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -6,6 +6,18 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exce
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DLOCKFILE_DIR=\"${localstatedir}/${PACKAGE_NAME}\"
 AM_CXXFLAGS = $(B10_CXXFLAGS)
+# If we use the shared-memory support, corresponding Boost library may
+# cause build failures especially if it's strict about warnings.  We've
+# detected it in ./configure and set BOOST_MAPPED_FILE_CXXFLAG to be more
+# lenient as necessary (specifically, when set it'd usually suppress -Werror).
+# This is a module wide setting, and has a possible bad side effect of hiding
+# issues in other files, but making it per-file seems to be too costly.
+# So we begin with the wider setting. If the side effect turns out to be too
+# harmful, we'll consider other measure, e.g, moving the related files into
+# a subdirectory.
+if USE_SHARED_MEMORY
+AM_CXXFLAGS += $(BOOST_MAPPED_FILE_CXXFLAG)
+endif
 
 lib_LTLIBRARIES = libb10-util.la
 libb10_util_la_SOURCES  = filename.h filename.cc
@@ -18,7 +30,9 @@ libb10_util_la_SOURCES += interprocess_sync_file.h interprocess_sync_file.cc
 libb10_util_la_SOURCES += interprocess_sync_null.h interprocess_sync_null.cc
 libb10_util_la_SOURCES += memory_segment.h
 libb10_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc
+if USE_SHARED_MEMORY
 libb10_util_la_SOURCES += memory_segment_mapped.h memory_segment_mapped.cc
+endif
 libb10_util_la_SOURCES += range_utilities.h
 libb10_util_la_SOURCES += hash/sha1.h hash/sha1.cc
 libb10_util_la_SOURCES += encode/base16_from_binary.h
diff --git a/src/lib/util/memory_segment.h b/src/lib/util/memory_segment.h
index 171145d..ae7bc91 100644
--- a/src/lib/util/memory_segment.h
+++ b/src/lib/util/memory_segment.h
@@ -62,7 +62,7 @@ public:
     /// \brief Destructor
     virtual ~MemorySegment() {}
 
-    /// \brief Allocate/acquire a segment of memory.
+    /// \brief Allocate/acquire a fragment of memory.
     ///
     /// The source of the memory is dependent on the implementation used.
     ///
@@ -82,6 +82,35 @@ public:
     /// been remapped and must be re-fetched via an already established
     /// named address using the \c getNamedAddress() method.
     ///
+    /// The intended use case of \c allocate() with the \c MemorySegmentGrown
+    /// exception is to build a complex object that would internally require
+    /// multiple calls to \c allocate():
+    ///
+    /// \code
+    /// ComplicatedStuff* stuff = NULL;
+    /// while (!stuff) { // this must eventually succeed or result in bad_alloc
+    ///     try {
+    ///         // create() is a factory method, takes a memory segment
+    ///         // and calls allocate() on it multiple times.  create()
+    ///         // provides exception guarantee in that any intermediately
+    ///         // allocated memory will be properly deallocate()-ed on
+    ///         // exception.
+    ///         stuff = ComplicatedStuff::create(mem_segment);
+    ///     } catch (const MemorySegmentGrown&) { /* just try again */ }
+    /// }
+    /// \endcode
+    ///
+    /// This way, \c create() can be written as if each call to \c allocate()
+    /// always succeeds.
+    ///
+    /// Alternatively, or in addition to this, we could introduce a "no throw"
+    /// version of this method with a way to tell the caller the reason of
+    /// any failure (whether it's really out of memory or just due to growing
+    /// the segment).  That would be more convenient if the caller wants to
+    /// deal with the failures per call basis rather than as a set of calls
+    /// like the above example.  At the moment, we don't expect to have
+    /// such use cases, so we only provide the exception version.
+    ///
     /// \throw std::bad_alloc The implementation cannot allocate the
     /// requested storage.
     /// \throw MemorySegmentGrown The memory segment doesn't have sufficient
@@ -100,6 +129,18 @@ public:
     /// use this argument in some implementations to test if all allocated
     /// memory was deallocated properly.
     ///
+    /// Specific implementation may also throw \c MemorySegmentError if it
+    /// encounters violation of implementation specific restrictions.
+    ///
+    /// In general, however, this method must succeed and exception free
+    /// as long as the caller passes valid parameters (\c ptr specifies
+    /// memory previously allocated and \c size is correct).
+    ///
+    /// \throw OutOfRange The passed size doesn't match the allocated memory
+    /// size (when identifiable for the implementation).
+    /// \throw MemorySegmentError Failure of implementation specific
+    /// validation.
+    ///
     /// \param ptr Pointer to the block of memory to free/release. This
     /// should be equal to a value returned by <code>allocate()</code>.
     /// \param size The size of the memory to be freed in bytes. This
@@ -130,7 +171,15 @@ public:
     /// to \c allocate().  The actual implementation is encouraged to detect
     /// violation of this restriction and signal it with an exception, but
     /// it's not an API requirement.  It's generally the caller's
-    /// responsibility to meet the restriction.
+    /// responsibility to meet the restriction.  Note that NULL is allowed
+    /// 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
+    /// has not been allocated for the specified name.  A subsequent call
+    /// to \c getNamedAddress() will return NULL for that name.
+    ///
+    /// \note Naming an address is intentionally separated from allocation
+    /// so that, for example, one module of a program can name a memory
+    /// region allocated by another module of the program.
     ///
     /// There can be an existing association for the name; in that case the
     /// association will be overridden with the newly given address.
@@ -147,14 +196,36 @@ public:
     /// application should interpret it just like the case of
     /// \c MemorySegmentGrown exception thrown from the \c allocate() method.
     ///
+    /// \note The behavior in case the internal segment grows is different
+    /// from that of \c allocate().  This is intentional.  In intended use
+    /// cases (for the moment) this method will be called independently,
+    /// rather than as part of a set of allocations.  It's also expected
+    /// that other raw memory addresses (which would have been invalidated
+    /// due to the change to the segment) won't be referenced directly
+    /// immediately after this call.  So, the caller should normally be able
+    /// to call this method as mostly never-fail one (except in case of real
+    /// memory exhaustion) and ignore the return value.
+    ///
     /// \throw std::bad_alloc Allocation of a segment space for the given name
     /// failed.
+    /// \throw InvalidParameter name is NULL.
+    /// \throw MemorySegmentError Failure of implementation specific
+    /// validation.
     ///
-    /// \param name A C string to be associated with \c addr.
+    /// \param name A C string to be associated with \c addr. Must not be NULL.
     /// \param addr A memory address returned by a prior call to \c allocate.
     /// \return true if the internal segment has grown to allocate space for
     /// the name; false otherwise (see above).
-    virtual bool setNamedAddress(const char* name, void* addr) = 0;
+    bool setNamedAddress(const char* name, void* addr) {
+        // This public method implements common validation.  The actual
+        // work specific to the derived segment is delegated to the
+        // corresponding protected method.
+        if (!name) {
+            isc_throw(InvalidParameter,
+                      "NULL name is given to setNamedAddress");
+        }
+        return (setNamedAddressImpl(name, addr));
+    }
 
     /// \brief Return the address in the segment that has the given name.
     ///
@@ -169,10 +240,21 @@ public:
     /// API doesn't guarantee that property.  In general, if this method
     /// throws it should be considered a fatal condition.
     ///
+    /// \throw InvalidParameter name is NULL.
+    ///
     /// \param name A C string of which the segment memory address is to be
-    /// returned.
+    /// returned.  Must not be NULL.
     /// \return The address associated with the name, or NULL if not found.
-    virtual void* getNamedAddress(const char* name) = 0;
+    void* getNamedAddress(const char* name) {
+        // This public method implements common validation.  The actual
+        // work specific to the derived segment is delegated to the
+        // corresponding protected method.
+        if (!name) {
+            isc_throw(InvalidParameter,
+                      "NULL name is given to getNamedAddress");
+        }
+        return (getNamedAddressImpl(name));
+    }
 
     /// \brief Delete a name previously associated with a segment address.
     ///
@@ -183,9 +265,32 @@ public:
     ///
     /// See \c getNamedAddress() about exception consideration.
     ///
+    /// \throw InvalidParameter name is NULL.
+    /// \throw MemorySegmentError Failure of implementation specific
+    /// validation.
+    ///
     /// \param name A C string of which the segment memory address is to be
-    /// deleted.
-    virtual bool clearNamedAddress(const char* name) = 0;
+    /// deleted. Must not be NULL.
+    bool clearNamedAddress(const char* name) {
+        // This public method implements common validation.  The actual
+        // work specific to the derived segment is delegated to the
+        // corresponding protected method.
+        if (!name) {
+            isc_throw(InvalidParameter,
+                      "NULL name is given to clearNamedAddress");
+        }
+        return (clearNamedAddressImpl(name));
+    }
+
+protected:
+    /// \brief Implementation of setNamedAddress beyond common validation.
+    virtual bool setNamedAddressImpl(const char* name, void* addr) = 0;
+
+    /// \brief Implementation of getNamedAddress beyond common validation.
+    virtual void* getNamedAddressImpl(const char* name) = 0;
+
+    /// \brief Implementation of clearNamedAddress beyond common validation.
+    virtual bool clearNamedAddressImpl(const char* name) = 0;
 };
 
 } // namespace util
diff --git a/src/lib/util/memory_segment_local.cc b/src/lib/util/memory_segment_local.cc
index 52de66b..81548fd 100644
--- a/src/lib/util/memory_segment_local.cc
+++ b/src/lib/util/memory_segment_local.cc
@@ -52,7 +52,7 @@ MemorySegmentLocal::allMemoryDeallocated() const {
 }
 
 void*
-MemorySegmentLocal::getNamedAddress(const char* name) {
+MemorySegmentLocal::getNamedAddressImpl(const char* name) {
     std::map<std::string, void*>::iterator found = named_addrs_.find(name);
     if (found != named_addrs_.end()) {
         return (found->second);
@@ -61,13 +61,13 @@ MemorySegmentLocal::getNamedAddress(const char* name) {
 }
 
 bool
-MemorySegmentLocal::setNamedAddress(const char* name, void* addr) {
+MemorySegmentLocal::setNamedAddressImpl(const char* name, void* addr) {
     named_addrs_[name] = addr;
     return (false);
 }
 
 bool
-MemorySegmentLocal::clearNamedAddress(const char* name) {
+MemorySegmentLocal::clearNamedAddressImpl(const char* name) {
     const size_t n_erased = named_addrs_.erase(name);
     return (n_erased != 0);
 }
diff --git a/src/lib/util/memory_segment_local.h b/src/lib/util/memory_segment_local.h
index 26fdd00..1db55a0 100644
--- a/src/lib/util/memory_segment_local.h
+++ b/src/lib/util/memory_segment_local.h
@@ -70,19 +70,24 @@ public:
     ///
     /// There's a small chance this method could throw std::bad_alloc.
     /// It should be considered a fatal error.
-    virtual void* getNamedAddress(const char* name);
+    virtual void* getNamedAddressImpl(const char* name);
 
     /// \brief Local segment version of setNamedAddress.
     ///
     /// This version does not validate the given address to see whether it
     /// belongs to this segment.
-    virtual bool setNamedAddress(const char* name, void* addr);
+    ///
+    /// This implementation of this method always returns \c false (but the
+    /// application should expect a return value of \c true unless it knows
+    /// the memory segment class is \c MemorySegmentLocal and needs to
+    /// exploit the fact).
+    virtual bool setNamedAddressImpl(const char* name, void* addr);
 
     /// \brief Local segment version of clearNamedAddress.
     ///
     /// There's a small chance this method could throw std::bad_alloc.
     /// It should be considered a fatal error.
-    virtual bool clearNamedAddress(const char* name);
+    virtual bool clearNamedAddressImpl(const char* name);
 
 private:
     // allocated_size_ can underflow, wrap around to max size_t (which
diff --git a/src/lib/util/memory_segment_mapped.cc b/src/lib/util/memory_segment_mapped.cc
index 6d0405c..a3d94e7 100644
--- a/src/lib/util/memory_segment_mapped.cc
+++ b/src/lib/util/memory_segment_mapped.cc
@@ -13,6 +13,9 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <util/memory_segment_mapped.h>
+#include <util/unittests/check_valgrind.h>
+
+#include <exceptions/exceptions.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/interprocess/exceptions.hpp>
@@ -28,6 +31,9 @@ using namespace boost::interprocess;
 
 namespace isc {
 namespace util {
+// Definition of class static constant so it can be referenced by address
+// or reference.
+const size_t MemorySegmentMapped::INITIAL_SIZE;
 
 // We customize managed_mapped_file to make it completely lock free.  In our
 // usage the application (or the system of applications) is expected to ensure
@@ -41,12 +47,21 @@ typedef basic_managed_mapped_file<char,
                                   iset_index> BaseSegment;
 
 struct MemorySegmentMapped::Impl {
-    Impl(const std::string& filename, size_t initial_size) :
+    // Constructor for create-only (and read-write) mode
+    Impl(const std::string& filename, create_only_t, size_t initial_size) :
+        read_only_(false), filename_(filename),
+        base_sgmt_(new BaseSegment(create_only, filename.c_str(),
+                                   initial_size))
+    {}
+
+    // Constructor for open-or-write (and read-write) mode
+    Impl(const std::string& filename, open_or_create_t, size_t initial_size) :
         read_only_(false), filename_(filename),
         base_sgmt_(new BaseSegment(open_or_create, filename.c_str(),
                                    initial_size))
     {}
 
+    // Constructor for existing segment, either read-only or read-write
     Impl(const std::string& filename, bool read_only) :
         read_only_(read_only), filename_(filename),
         base_sgmt_(read_only ?
@@ -58,11 +73,15 @@ struct MemorySegmentMapped::Impl {
     void growSegment() {
         // We first need to unmap it before calling grow().
         const size_t prev_size = base_sgmt_->get_size();
-        base_sgmt_->flush();
         base_sgmt_.reset();
 
+        // Double the segment size.  In theory, this process could repeat
+        // so many times, counting to "infinity", and new_size eventually
+        // overflows.  That would cause a harsh disruption or unexpected
+        // behavior.  But we basically assume grow() would fail before this
+        // happens, so we assert it shouldn't happen.
         const size_t new_size = prev_size * 2;
-        assert(new_size != 0); // assume grow fails before size overflow
+        assert(new_size != 0);
 
         if (!BaseSegment::grow(filename_.c_str(), new_size - prev_size)) {
             throw std::bad_alloc();
@@ -89,7 +108,7 @@ struct MemorySegmentMapped::Impl {
 };
 
 MemorySegmentMapped::MemorySegmentMapped(const std::string& filename) :
-    impl_(0)
+    impl_(NULL)
 {
     try {
         impl_ = new Impl(filename, true);
@@ -101,14 +120,25 @@ MemorySegmentMapped::MemorySegmentMapped(const std::string& filename) :
 }
 
 MemorySegmentMapped::MemorySegmentMapped(const std::string& filename,
-                                         bool create, size_t initial_size) :
-    impl_(0)
+                                         OpenMode mode, size_t initial_size) :
+    impl_(NULL)
 {
     try {
-        if (create) {
-            impl_ = new Impl(filename, initial_size);
-        } else {
+        switch (mode) {
+        case OPEN_FOR_WRITE:
             impl_ = new Impl(filename, false);
+            break;
+        case OPEN_OR_CREATE:
+            impl_ = new Impl(filename, open_or_create, initial_size);
+            break;
+        case CREATE_ONLY:
+            // Remove any existing file then create a new one
+            file_mapping::remove(filename.c_str());
+            impl_ = new Impl(filename, create_only, initial_size);
+            break;
+        default:
+            isc_throw(InvalidParameter,
+                      "invalid open mode for MemorySegmentMapped: " << mode);
         }
     } catch (const boost::interprocess::interprocess_exception& ex) {
         isc_throw(MemorySegmentOpenError,
@@ -127,7 +157,7 @@ MemorySegmentMapped::~MemorySegmentMapped() {
 void*
 MemorySegmentMapped::allocate(size_t size) {
     if (impl_->read_only_) {
-        isc_throw(InvalidOperation, "allocate attempt on read-only segment");
+        isc_throw(MemorySegmentError, "allocate attempt on read-only segment");
     }
 
     // We explicitly check the free memory size; it appears
@@ -153,10 +183,11 @@ MemorySegmentMapped::allocate(size_t size) {
 void
 MemorySegmentMapped::deallocate(void* ptr, size_t) {
     if (impl_->read_only_) {
-        isc_throw(InvalidOperation, "allocate attempt on read-only segment");
+        isc_throw(MemorySegmentError,
+                  "deallocate attempt on read-only segment");
     }
 
-    // the underlying deallocate() would deal with the case where ptr == 0,
+    // the underlying deallocate() would deal with the case where ptr == NULL,
     // but it's an undocumented behavior, so we handle it ourselves for safety.
     if (!ptr) {
         return;
@@ -171,19 +202,19 @@ MemorySegmentMapped::allMemoryDeallocated() const {
 }
 
 void*
-MemorySegmentMapped::getNamedAddress(const char* name) {
+MemorySegmentMapped::getNamedAddressImpl(const char* name) {
     offset_ptr<void>* storage =
         impl_->base_sgmt_->find<offset_ptr<void> >(name).first;
     if (storage) {
         return (storage->get());
     }
-    return (0);
+    return (NULL);
 }
 
 bool
-MemorySegmentMapped::setNamedAddress(const char* name, void* addr) {
+MemorySegmentMapped::setNamedAddressImpl(const char* name, void* addr) {
     if (impl_->read_only_) {
-        isc_throw(InvalidOperation, "setNamedAddress on read-only segment");
+        isc_throw(MemorySegmentError, "setNamedAddress on read-only segment");
     }
 
     if (addr && !impl_->base_sgmt_->belongs_to_segment(addr)) {
@@ -206,9 +237,10 @@ MemorySegmentMapped::setNamedAddress(const char* name, void* addr) {
 }
 
 bool
-MemorySegmentMapped::clearNamedAddress(const char* name) {
+MemorySegmentMapped::clearNamedAddressImpl(const char* name) {
     if (impl_->read_only_) {
-        isc_throw(InvalidOperation, "clearNamedAddress on read-only segment");
+        isc_throw(MemorySegmentError,
+                  "clearNamedAddress on read-only segment");
     }
 
     return (impl_->base_sgmt_->destroy<offset_ptr<void> >(name));
@@ -217,12 +249,16 @@ MemorySegmentMapped::clearNamedAddress(const char* name) {
 void
 MemorySegmentMapped::shrinkToFit() {
     if (impl_->read_only_) {
-        isc_throw(InvalidOperation, "shrinkToFit on read-only segment");
+        isc_throw(MemorySegmentError, "shrinkToFit on read-only segment");
     }
 
     // It appears an assertion failure is triggered within Boost if the size
-    // is too small.  To work this around we'll make it no-op if the size is
-    // already reasonably small.
+    // is too small (happening if shrink_to_fit() is called twice without
+    // allocating any memory from the shrunk segment).  To work this around
+    // we'll make it no-op if the size is already reasonably small.
+    // Using INITIAL_SIZE is not 100% reliable as it's irrelevant to the
+    // internal constraint of the Boost implementation.  But, in practice,
+    // it should be sufficiently large and safe.
     if (getSize() < INITIAL_SIZE) {
         return;
     }
@@ -232,7 +268,7 @@ MemorySegmentMapped::shrinkToFit() {
 
     BaseSegment::shrink_to_fit(impl_->filename_.c_str());
     try {
-        // Remap the grown file; this should succeed, but it's not 100%
+        // Remap the shrunk file; this should succeed, but it's not 100%
         // guaranteed.  If it fails we treat it as if we fail to create
         // the new segment.
         impl_->base_sgmt_.reset(
@@ -250,7 +286,8 @@ MemorySegmentMapped::getSize() const {
 
 size_t
 MemorySegmentMapped::getCheckSum() const {
-    const size_t pagesize = boost::interprocess::mapped_region::get_page_size();
+    const size_t pagesize =
+        boost::interprocess::mapped_region::get_page_size();
     const uint8_t* const cp_begin = static_cast<const uint8_t*>(
         impl_->base_sgmt_->get_address());
     const uint8_t* const cp_end = cp_begin + impl_->base_sgmt_->get_size();
diff --git a/src/lib/util/memory_segment_mapped.h b/src/lib/util/memory_segment_mapped.h
index 3ced535..476974c 100644
--- a/src/lib/util/memory_segment_mapped.h
+++ b/src/lib/util/memory_segment_mapped.h
@@ -16,7 +16,6 @@
 #define MEMORY_SEGMENT_MAPPED_H
 
 #include <util/memory_segment.h>
-#include <exceptions/exceptions.h>
 
 #include <boost/noncopyable.hpp>
 
@@ -51,6 +50,16 @@ public:
     /// sufficiently but not too large.
     static const size_t INITIAL_SIZE = 32768;
 
+    /// \brief Open modes of \c MemorySegmentMapped.
+    ///
+    /// These modes matter only for \c MemorySegmentMapped to be opened
+    /// in read-write mode, and specify further details of open operation.
+    enum OpenMode {
+        OPEN_FOR_WRITE = 0,     ///< Open only.  File must exist.
+        OPEN_OR_CREATE,         ///< If file doesn't exist it's created.
+        CREATE_ONLY ///< New file is created; existing one will be removed.
+    };
+
     /// \brief Constructor in the read-only mode.
     ///
     /// This constructor will map the content of the given file into memory
@@ -76,20 +85,40 @@ public:
     ///
     /// This is similar to the read-only version of the constructor, but
     /// does not have the restrictions that the read-only version has.
-    /// If \c create is true and the specified file does not exist, this
-    /// method tries to create a new file of the name and build internal
-    /// data on it so that the file will be mappable by this class
-    /// object.  If \c create is false, the specified file must exist
+    ///
+    /// The \c mode parameter specifies further details of how the segment
+    /// should be opened.
+    /// - OPEN_FOR_WRITE: this is open-only mode.  The file must exist,
+    ///   and it will be opened without any initial modification.
+    /// - OPEN_OR_CREATE: similar to OPEN_FOR_WRITE, but if the file does not
+    ///   exist, a new one will be created.  An existing file will be used
+    ///   any initial modification.
+    /// - CREATE_ONLY: a new file (of the given file name) will be created;
+    ///   any existing file of the same name will be removed.
+    ///
+    /// If OPEN_FOR_WRITE is specified, the specified file must exist
     /// and be writable, and have been previously initialized by this
-    /// version of constructor with \c create being true.  If any of
-    /// these conditions is not met, \c MemorySegmentOpenError exception
+    /// version of constructor either with OPEN_OR_CREATE or CREATE_ONLY.
+    /// If the mode is OPEN_OR_CREATE or CREATE_ONLY, and the file needs
+    /// to be created, then this method tries to create a new file of the
+    /// name and build internal data on it so that the file will be mappable
+    /// by this class object.  If any of these conditions is not met, or
+    /// create or initialization fails, \c MemorySegmentOpenError exception
     /// will be thrown.
     ///
+    /// When initial_size is specified but is too small (including a value of
+    /// 0), the underlying Boost library will reject it, and this constructor
+    /// throws \c MemorySegmentOpenError exception.  The Boost documentation
+    /// does not specify how large it should be, but the default
+    /// \c INITIAL_SIZE should be sufficiently large in practice.
+    ///
+    /// \throw MemorySegmentOpenError see the description.
+    ///
     /// \param filename The file name to be mapped to memory.
-    /// \param create If true and the file does not exist, a new one is created.
+    /// \param mode Open mode (see the description).
     /// \param initial_size Specifies the size of the newly created file;
-    /// ignored if \c create is false.
-    MemorySegmentMapped(const std::string& filename, bool create,
+    /// ignored if \c mode is OPEN_FOR_WRITE.
+    MemorySegmentMapped(const std::string& filename, OpenMode mode,
                         size_t initial_size = INITIAL_SIZE);
 
     /// \brief Destructor.
@@ -105,7 +134,7 @@ public:
     /// This version can throw \c MemorySegmentGrown.
     ///
     /// This method cannot be called if the segment object is created in the
-    /// read-only mode; in that case InvalidOperation will be thrown.
+    /// read-only mode; in that case MemorySegmentError will be thrown.
     virtual void* allocate(size_t size);
 
     /// \brief Deallocate/release a segment of memory.
@@ -124,7 +153,7 @@ public:
     /// via \c getNamedAddress().
     ///
     /// This method cannot be called if the segment object is created in the
-    /// read-only mode; in that case InvalidOperation will be thrown.
+    /// read-only mode; in that case MemorySegmentError will be thrown.
     virtual void deallocate(void* ptr, size_t size);
 
     virtual bool allMemoryDeallocated() const;
@@ -134,21 +163,23 @@ public:
     /// This implementation detects if \c addr is invalid (see the base class
     /// description) and throws \c MemorySegmentError in that case.
     ///
-    /// This version can actually return true.
+    /// This version can actually return true (see also \c MemorySegmentLocal
+    /// version of the method).
     ///
     /// This method cannot be called if the segment object is created in the
-    /// read-only mode; in that case InvalidOperation will be thrown.
-    virtual bool setNamedAddress(const char* name, void* addr);
+    /// read-only mode; in that case MemorySegmentError will be thrown.
+    virtual bool setNamedAddressImpl(const char* name, void* addr);
 
     /// \brief Mapped segment version of getNamedAddress.
     ///
     /// This version never throws.
-    virtual void* getNamedAddress(const char* name);
+    virtual void* getNamedAddressImpl(const char* name);
 
     /// \brief Mapped segment version of clearNamedAddress.
     ///
-    /// This version never throws.
-    virtual bool clearNamedAddress(const char* name);
+    /// This method cannot be called if the segment object is created in the
+    /// read-only mode; in that case MemorySegmentError will be thrown.
+    virtual bool clearNamedAddressImpl(const char* name);
 
     /// \brief Shrink the underlying mapped segment to actually used size.
     ///
@@ -167,7 +198,9 @@ public:
     /// segment is not usable anymore.
     ///
     /// This method cannot be called if the segment object is created in the
-    /// read-only mode; in that case InvalidOperation will be thrown.
+    /// read-only mode; in that case MemorySegmentError will be thrown.
+    ///
+    /// \throw MemorySegmentError see the description.
     void shrinkToFit();
 
     /// \brief Return the actual segment size.
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index ba83dc4..3ee16f9 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -34,7 +34,9 @@ run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += interprocess_sync_file_unittest.cc
 run_unittests_SOURCES += interprocess_sync_null_unittest.cc
 run_unittests_SOURCES += memory_segment_local_unittest.cc
+if USE_SHARED_MEMORY
 run_unittests_SOURCES += memory_segment_mapped_unittest.cc
+endif
 run_unittests_SOURCES += memory_segment_common_unittest.h
 run_unittests_SOURCES += memory_segment_common_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
@@ -44,6 +46,7 @@ run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += time_utilities_unittest.cc
 run_unittests_SOURCES += range_utilities_unittest.cc
+run_unittests_SOURCES += interprocess_util.h interprocess_util.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/util/tests/interprocess_sync_file_unittest.cc b/src/lib/util/tests/interprocess_sync_file_unittest.cc
index 6f23558..38d9026 100644
--- a/src/lib/util/tests/interprocess_sync_file_unittest.cc
+++ b/src/lib/util/tests/interprocess_sync_file_unittest.cc
@@ -12,48 +12,20 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include "util/interprocess_sync_file.h"
+#include <util/interprocess_sync_file.h>
 
 #include <util/unittests/check_valgrind.h>
+#include <util/tests/interprocess_util.h>
 #include <gtest/gtest.h>
 #include <unistd.h>
 
 using namespace std;
+using isc::util::test::parentReadState;
 
 namespace isc {
 namespace util {
 
 namespace {
-unsigned char
-parentReadLockedState (int fd) {
-  unsigned char locked = 0xff;
-
-  fd_set rfds;
-  FD_ZERO(&rfds);
-  FD_SET(fd, &rfds);
-
-  // We use select() here to wait for new data on the input end of
-  // the pipe. We wait for 5 seconds (an arbitrary value) for input
-  // data, and continue if no data is available. This is done so
-  // that read() is not blocked due to some issue in the child
-  // process (and the tests continue running).
-
-  struct timeval tv;
-  tv.tv_sec = 5;
-  tv.tv_usec = 0;
-
-  const int nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
-  EXPECT_EQ(1, nfds);
-
-  if (nfds == 1) {
-      // Read status
-      ssize_t bytes_read = read(fd, &locked, sizeof(locked));
-      EXPECT_EQ(sizeof(locked), bytes_read);
-  }
-
-  return (locked);
-}
-
 TEST(InterprocessSyncFileTest, TestLock) {
     InterprocessSyncFile sync("test");
     InterprocessSyncLocker locker(sync);
@@ -99,7 +71,7 @@ TEST(InterprocessSyncFileTest, TestLock) {
             // Parent reads from pipe
             close(fds[1]);
 
-            const unsigned char locked = parentReadLockedState(fds[0]);
+            const unsigned char locked = parentReadState(fds[0]);
 
             close(fds[0]);
 
@@ -163,7 +135,7 @@ TEST(InterprocessSyncFileTest, TestMultipleFilesForked) {
             // Parent reads from pipe
             close(fds[1]);
 
-            const unsigned char locked = parentReadLockedState(fds[0]);
+            const unsigned char locked = parentReadState(fds[0]);
 
             close(fds[0]);
 
diff --git a/src/lib/util/tests/interprocess_util.cc b/src/lib/util/tests/interprocess_util.cc
new file mode 100644
index 0000000..dfb04b7
--- /dev/null
+++ b/src/lib/util/tests/interprocess_util.cc
@@ -0,0 +1,48 @@
+// 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 <gtest/gtest.h>
+
+#include <sys/select.h>
+#include <cstddef>
+
+namespace isc {
+namespace util {
+namespace test {
+
+unsigned char
+parentReadState(int fd) {
+  unsigned char result = 0xff;
+
+  fd_set rfds;
+  FD_ZERO(&rfds);
+  FD_SET(fd, &rfds);
+
+  struct timeval tv = {5, 0};
+
+  const int nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
+  EXPECT_EQ(1, nfds);
+
+  if (nfds == 1) {
+      // Read status
+      const ssize_t bytes_read = read(fd, &result, sizeof(result));
+      EXPECT_EQ(sizeof(result), bytes_read);
+  }
+
+  return (result);
+}
+
+}
+}
+}
diff --git a/src/lib/util/tests/interprocess_util.h b/src/lib/util/tests/interprocess_util.h
new file mode 100644
index 0000000..286f9cf
--- /dev/null
+++ b/src/lib/util/tests/interprocess_util.h
@@ -0,0 +1,31 @@
+// 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.
+
+namespace isc {
+namespace util {
+namespace test {
+/// \brief A helper utility for a simple synchronization with another process.
+///
+/// It waits for incoming data on a given file descriptor up to 5 seconds
+/// (arbitrary choice), read one byte data, and return it to the caller.
+/// On any failure it returns 0xff (255), so the sender process should use
+/// a different value to pass.
+unsigned char parentReadState(int fd);
+}
+}
+}
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/tests/memory_segment_common_unittest.cc b/src/lib/util/tests/memory_segment_common_unittest.cc
index 45bef7b..d47a3e2 100644
--- a/src/lib/util/tests/memory_segment_common_unittest.cc
+++ b/src/lib/util/tests/memory_segment_common_unittest.cc
@@ -14,6 +14,8 @@
 
 #include <util/memory_segment.h>
 
+#include <exceptions/exceptions.h>
+
 #include <gtest/gtest.h>
 
 #include <cstring>
@@ -25,15 +27,22 @@ namespace test {
 
 void
 checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
+    // NULL name is not allowed.
+    EXPECT_THROW(segment.getNamedAddress(NULL), InvalidParameter);
+
     // If the name does not exist, NULL should be returned.
-    EXPECT_EQ(static_cast<void*>(0), segment.getNamedAddress("test address"));
+    EXPECT_EQ(static_cast<void*>(NULL),
+              segment.getNamedAddress("test address"));
 
     // Now set it
     void* ptr32 = segment.allocate(sizeof(uint32_t));
     const uint32_t test_val = 42;
-    std::memcpy(ptr32, &test_val, sizeof(test_val));
+    *static_cast<uint32_t*>(ptr32) = test_val;
     EXPECT_FALSE(segment.setNamedAddress("test address", ptr32));
 
+    // NULL name isn't allowed.
+    EXPECT_THROW(segment.setNamedAddress(NULL, ptr32), InvalidParameter);
+
     // we can now get it; the stored value should be intact.
     EXPECT_EQ(ptr32, segment.getNamedAddress("test address"));
     EXPECT_EQ(test_val, *static_cast<const uint32_t*>(ptr32));
@@ -41,21 +50,23 @@ checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
     // Override it.
     void* ptr16 = segment.allocate(sizeof(uint16_t));
     const uint16_t test_val16 = 4200;
-    std::memcpy(ptr16, &test_val16, sizeof(test_val16));
+    *static_cast<uint16_t*>(ptr16) = test_val16;
     EXPECT_FALSE(segment.setNamedAddress("test address", ptr16));
     EXPECT_EQ(ptr16, segment.getNamedAddress("test address"));
     EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(ptr16));
 
     // Clear it.  Then we won't be able to find it any more.
     EXPECT_TRUE(segment.clearNamedAddress("test address"));
-    EXPECT_EQ(static_cast<void*>(0), segment.getNamedAddress("test address"));
+    EXPECT_EQ(static_cast<void*>(NULL),
+              segment.getNamedAddress("test address"));
 
     // duplicate attempt of clear will result in false as it doesn't exist.
     EXPECT_FALSE(segment.clearNamedAddress("test address"));
 
     // Setting NULL is okay.
     EXPECT_FALSE(segment.setNamedAddress("null address", 0));
-    EXPECT_EQ(static_cast<void*>(0), segment.getNamedAddress("null address"));
+    EXPECT_EQ(static_cast<void*>(NULL),
+              segment.getNamedAddress("null address"));
 
     // If the underlying implementation performs explicit check against
     // out-of-segment address, confirm the behavior.
@@ -71,6 +82,8 @@ checkSegmentNamedAddress(MemorySegment& segment, bool out_of_segment_ok) {
     segment.deallocate(ptr16, sizeof(uint16_t));  // not yet
     EXPECT_FALSE(segment.allMemoryDeallocated());
     EXPECT_TRUE(segment.clearNamedAddress("null address"));
+    // null name isn't allowed:
+    EXPECT_THROW(segment.clearNamedAddress(NULL), InvalidParameter);
     EXPECT_TRUE(segment.allMemoryDeallocated()); // now everything is gone
 }
 
diff --git a/src/lib/util/tests/memory_segment_mapped_unittest.cc b/src/lib/util/tests/memory_segment_mapped_unittest.cc
index cf736a9..b1e3ab5 100644
--- a/src/lib/util/tests/memory_segment_mapped_unittest.cc
+++ b/src/lib/util/tests/memory_segment_mapped_unittest.cc
@@ -14,6 +14,7 @@
 
 #include <util/tests/memory_segment_common_unittest.h>
 #include <util/unittests/check_valgrind.h>
+#include <util/tests/interprocess_util.h>
 
 #include <util/memory_segment_mapped.h>
 #include <exceptions/exceptions.h>
@@ -31,12 +32,25 @@
 #include <stdexcept>
 #include <fstream>
 #include <string>
+#include <vector>
+#include <map>
+
 #include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
 
 using namespace isc::util;
 using boost::scoped_ptr;
+using isc::util::test::parentReadState;
 
 namespace {
+// Shortcut to keep code shorter
+const MemorySegmentMapped::OpenMode OPEN_FOR_WRITE =
+    MemorySegmentMapped::OPEN_FOR_WRITE;
+const MemorySegmentMapped::OpenMode OPEN_OR_CREATE =
+    MemorySegmentMapped::OPEN_OR_CREATE;
+const MemorySegmentMapped::OpenMode CREATE_ONLY =
+    MemorySegmentMapped::CREATE_ONLY;
 
 const char* const mapped_file = TEST_DATA_BUILDDIR "/test.mapped";
 const size_t DEFAULT_INITIAL_SIZE = 32 * 1024; // intentionally hardcoded
@@ -57,7 +71,7 @@ protected:
     void resetSegment() {
         segment_.reset();
         boost::interprocess::file_mapping::remove(mapped_file);
-        segment_.reset(new MemorySegmentMapped(mapped_file, true));
+        segment_.reset(new MemorySegmentMapped(mapped_file, OPEN_OR_CREATE));
     }
 
     scoped_ptr<MemorySegmentMapped> segment_;
@@ -87,7 +101,7 @@ TEST_F(MemorySegmentMappedTest, createAndModify) {
 
         // re-open it in read-write mode, but don't try to create it
         // this time.
-        segment_.reset(new MemorySegmentMapped(mapped_file, false));
+        segment_.reset(new MemorySegmentMapped(mapped_file, OPEN_FOR_WRITE));
     }
 }
 
@@ -98,18 +112,31 @@ TEST_F(MemorySegmentMappedTest, createWithSize) {
     // the size is actually the specified one.
     const size_t new_size = 64 * 1024;
     EXPECT_NE(new_size, segment_->getSize());
-    segment_.reset(new MemorySegmentMapped(mapped_file, true, new_size));
+    segment_.reset(new MemorySegmentMapped(mapped_file, OPEN_OR_CREATE,
+                                           new_size));
     EXPECT_EQ(new_size, segment_->getSize());
 }
 
+TEST_F(MemorySegmentMappedTest, createOnly) {
+    // First, allocate some data in the existing segment
+    EXPECT_TRUE(segment_->allocate(16));
+    // Close it, and then open it again in the create-only mode.  the existing
+    // file should be internally removed, and so the resulting segment
+    // should be "empty" (all deallocated).
+    segment_.reset();
+    segment_.reset(new MemorySegmentMapped(mapped_file, CREATE_ONLY));
+    EXPECT_TRUE(segment_->allMemoryDeallocated());
+}
+
 TEST_F(MemorySegmentMappedTest, openFail) {
     // The given file is directory
-    EXPECT_THROW(MemorySegmentMapped("/", true), MemorySegmentOpenError);
+    EXPECT_THROW(MemorySegmentMapped("/", OPEN_OR_CREATE),
+                 MemorySegmentOpenError);
 
     // file doesn't exist and directory isn't writable (we assume the
     // following path is not writable for the user running the test).
-    EXPECT_THROW(MemorySegmentMapped("/random-glkwjer098/test.mapped", true),
-                 MemorySegmentOpenError);
+    EXPECT_THROW(MemorySegmentMapped("/random-glkwjer098/test.mapped",
+                                     OPEN_OR_CREATE), MemorySegmentOpenError);
 
     // It should fail when file doesn't exist and it's read-only (so
     // open-only).
@@ -118,9 +145,21 @@ TEST_F(MemorySegmentMappedTest, openFail) {
     // Likewise, it should fail in read-write mode when creation is
     // suppressed.
     EXPECT_THROW(MemorySegmentMapped(TEST_DATA_BUILDDIR "/nosuchfile.mapped",
-                                     false),
+                                     OPEN_FOR_WRITE), MemorySegmentOpenError);
+
+    // creating with a very small size fails (for sure about 0, and other
+    // small values should also make it fail, but it's internal restriction
+    // of Boost and cannot be predictable).
+    EXPECT_THROW(MemorySegmentMapped(mapped_file, OPEN_OR_CREATE, 0),
                  MemorySegmentOpenError);
 
+    // invalid read-write mode
+    EXPECT_THROW(MemorySegmentMapped(
+                     mapped_file,
+                     static_cast<MemorySegmentMapped::OpenMode>(
+                         static_cast<int>(CREATE_ONLY) + 1)),
+                 isc::InvalidParameter);
+
     // Close the existing segment, break its file with bogus data, and
     // try to reopen.  It should fail with exception whether in the
     // read-only or read-write, or "create if not exist" mode.
@@ -129,9 +168,9 @@ TEST_F(MemorySegmentMappedTest, openFail) {
     ofs << std::string(1024, 'x');
     ofs.close();
     EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file), MemorySegmentOpenError);
-    EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, false),
+    EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, OPEN_FOR_WRITE),
                  MemorySegmentOpenError);
-    EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, true),
+    EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, OPEN_OR_CREATE),
                  MemorySegmentOpenError);
 }
 
@@ -152,7 +191,7 @@ TEST_F(MemorySegmentMappedTest, allocate) {
 
     // Now, the allocation should now succeed.
     void* ptr = segment_->allocate(prev_size + 1);
-    EXPECT_NE(static_cast<void*>(0), ptr);
+    EXPECT_NE(static_cast<void*>(NULL), ptr);
     EXPECT_FALSE(segment_->allMemoryDeallocated());
 
     // Same set of checks, but for a larger size.
@@ -162,7 +201,7 @@ TEST_F(MemorySegmentMappedTest, allocate) {
     EXPECT_EQ(prev_size * 16, segment_->getSize());
     // And allocate() should now succeed.
     ptr = segment_->allocate(prev_size * 10);
-    EXPECT_NE(static_cast<void*>(0), ptr);
+    EXPECT_NE(static_cast<void*>(NULL), ptr);
 
     // (we'll left the regions created in the file there; the entire file
     // will be removed at the end of the test)
@@ -186,7 +225,7 @@ TEST_F(MemorySegmentMappedTest, DISABLED_allocateHuge) {
 
 TEST_F(MemorySegmentMappedTest, badDeallocate) {
     void* ptr = segment_->allocate(4);
-    EXPECT_NE(static_cast<void*>(0), ptr);
+    EXPECT_NE(static_cast<void*>(NULL), ptr);
 
     segment_->deallocate(ptr, 4); // this is okay
     // This is duplicate dealloc; should trigger assertion failure.
@@ -200,7 +239,7 @@ TEST_F(MemorySegmentMappedTest, badDeallocate) {
     // default).
     if (!isc::util::unittests::runningOnValgrind()) {
         ptr = segment_->allocate(4);
-        EXPECT_NE(static_cast<void*>(0), ptr);
+        EXPECT_NE(static_cast<void*>(NULL), ptr);
         EXPECT_DEATH_IF_SUPPORTED({
                 segment_->deallocate(static_cast<char*>(ptr) + 1, 3);
             }, "");
@@ -209,11 +248,41 @@ TEST_F(MemorySegmentMappedTest, badDeallocate) {
 
     // Invalid size; this implementation doesn't detect such errors.
     ptr = segment_->allocate(4);
-    EXPECT_NE(static_cast<void*>(0), ptr);
+    EXPECT_NE(static_cast<void*>(NULL), ptr);
     segment_->deallocate(ptr, 8);
     EXPECT_TRUE(segment_->allMemoryDeallocated());
 }
 
+// A helper of namedAddress.
+void
+checkNamedData(const std::string& name, const std::vector<uint8_t>& data,
+               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()));
+
+    // Open a separate read-only segment and checks the same named data
+    // Since the mapped space should be different, the resulting bare address
+    // from getNamedAddress should also be different, but it should actually
+    // point to the same data.
+    // Note: it's mostly violation of the API assumption to open read-only
+    // and read-write segments at the same time, but unless we modify the
+    // segment throughout the lifetime of the read-only segment, it should
+    // work.
+    scoped_ptr<MemorySegmentMapped> segment_ro(
+        new MemorySegmentMapped(mapped_file));
+    void* dp2 = segment_ro->getNamedAddress(name.c_str());
+    ASSERT_TRUE(dp2);
+    EXPECT_NE(dp, dp2);
+    EXPECT_EQ(0, std::memcmp(dp2, &data[0], data.size()));
+    segment_ro.reset();
+
+    if (delete_after_check) {
+        sgmt.deallocate(dp, data.size());
+    }
+}
+
 TEST_F(MemorySegmentMappedTest, namedAddress) {
     // common test cases
     isc::util::test::checkSegmentNamedAddress(*segment_, false);
@@ -221,7 +290,7 @@ TEST_F(MemorySegmentMappedTest, namedAddress) {
     // Set it again and read it in the read-only mode.
     void* ptr16 = segment_->allocate(sizeof(uint16_t));
     const uint16_t test_val16 = 42000;
-    std::memcpy(ptr16, &test_val16, sizeof(test_val16));
+    *static_cast<uint16_t*>(ptr16) = test_val16;
     EXPECT_FALSE(segment_->setNamedAddress("test address", ptr16));
     MemorySegmentMapped segment_ro(mapped_file);
     EXPECT_NE(static_cast<void*>(NULL),
@@ -234,12 +303,112 @@ TEST_F(MemorySegmentMappedTest, namedAddress) {
     // segment extension.
     segment_.reset();
     boost::interprocess::file_mapping::remove(mapped_file);
-    segment_.reset(new MemorySegmentMapped(mapped_file, true, 1024));
+    segment_.reset(new MemorySegmentMapped(mapped_file, OPEN_OR_CREATE, 1024));
     const std::string long_name(1025, 'x'); // definitely larger than segment
     // setNamedAddress should return true, indicating segment has grown.
-    EXPECT_TRUE(segment_->setNamedAddress(long_name.c_str(), 0));
-    EXPECT_EQ(static_cast<void*>(0),
+    EXPECT_TRUE(segment_->setNamedAddress(long_name.c_str(), NULL));
+    EXPECT_EQ(static_cast<void*>(NULL),
               segment_->getNamedAddress(long_name.c_str()));
+
+    // Check contents pointed by named addresses survive growing and
+    // shrinking segment.
+    segment_.reset();
+    boost::interprocess::file_mapping::remove(mapped_file);
+    segment_.reset(new MemorySegmentMapped(mapped_file, OPEN_OR_CREATE));
+    std::map<std::string, std::vector<uint8_t> > data_list;
+    data_list["data1"] =
+        std::vector<uint8_t>(80); // arbitrarily chosen small data
+    data_list["data2"] =
+        std::vector<uint8_t>(5000); // larger than usual segment sz
+    data_list["data3"] =
+        std::vector<uint8_t>(65535); // bigger than most usual data
+    bool grown = false;
+
+    // Allocate memory and store data
+    for (std::map<std::string, std::vector<uint8_t> >::iterator it
+             = data_list.begin();
+         it != data_list.end();
+         ++it)
+    {
+        std::vector<uint8_t>& data = it->second;
+        for (int i = 0; i < data.size(); ++i) {
+            data[i] = i;
+        }
+        void *dp = NULL;
+        while (!dp) {
+            try {
+                dp = segment_->allocate(data.size());
+                std::memcpy(dp, &data[0], data.size());
+                segment_->setNamedAddress(it->first.c_str(), dp);
+            } catch (const MemorySegmentGrown&) {
+                grown = true;
+            }
+        }
+    }
+    // Confirm there's at least one segment extension
+    EXPECT_TRUE(grown);
+    // Check named data are still valid
+    for (std::map<std::string, std::vector<uint8_t> >::iterator it
+             = data_list.begin();
+         it != data_list.end();
+         ++it)
+    {
+        checkNamedData(it->first, it->second, *segment_);
+    }
+    // Confirm they are still valid, while we shrink the segment
+    const char* const names[] = { "data3", "data2", "data1", NULL };
+    for (int i = 0; names[i]; ++i) {
+        checkNamedData(names[i], data_list[names[i]], *segment_, true);
+        segment_->shrinkToFit();
+    }
+}
+
+TEST_F(MemorySegmentMappedTest, multiProcess) {
+    // Test using fork() doesn't work well on valgrind
+    if (isc::util::unittests::runningOnValgrind()) {
+        return;
+    }
+
+    // allocate some data and name its address
+    void* ptr = segment_->allocate(sizeof(uint32_t));
+    *static_cast<uint32_t*>(ptr) = 424242;
+    segment_->setNamedAddress("test address", ptr);
+
+    // reopen it in read-only.  our intended use case is to have one or
+    // more reader process or at most one exclusive writer process.  so we
+    // don't mix reader and writer.
+    segment_.reset();
+    segment_.reset(new MemorySegmentMapped(mapped_file));
+    ptr = segment_->getNamedAddress("test address");
+    ASSERT_TRUE(ptr);
+    EXPECT_EQ(424242, *static_cast<const uint32_t*>(ptr));
+
+    // Spawn another process and have it open and read the same data
+    int pipes[2];
+    EXPECT_EQ(0, pipe(pipes));
+    const pid_t child_pid = fork();
+    ASSERT_NE(-1, child_pid);
+    if (child_pid == 0) {       // child
+        close(pipes[0]);
+        MemorySegmentMapped sgmt(mapped_file);
+        void* ptr_child = segment_->getNamedAddress("test address");
+        EXPECT_TRUE(ptr_child);
+        if (ptr_child) {
+            const uint32_t val = *static_cast<const uint32_t*>(ptr_child);
+            EXPECT_EQ(424242, val);
+            if (val == 424242) {
+                // tell the parent it succeeded using a result code other
+                // than 255.
+                const char ok = 0;
+                EXPECT_EQ(1, write(pipes[1], &ok, sizeof(ok)));
+            }
+        }
+        close(pipes[1]);
+        exit(0);
+    }
+    // parent: wait for the completion of the child and checks the result.
+    close(pipes[1]);
+    EXPECT_EQ(0, parentReadState(pipes[0]));
 }
 
 TEST_F(MemorySegmentMappedTest, nullDeallocate) {
@@ -272,18 +441,18 @@ TEST_F(MemorySegmentMappedTest, violateReadOnly) {
     // attempts are prohibited. When detectable it must result in an
     // exception.
     EXPECT_THROW(MemorySegmentMapped(mapped_file).allocate(16),
-                 isc::InvalidOperation);
+                 MemorySegmentError);
     // allocation that would otherwise require growing the segment; permission
     // check should be performed before that.
     EXPECT_THROW(MemorySegmentMapped(mapped_file).
-                 allocate(DEFAULT_INITIAL_SIZE * 2),
-                 isc::InvalidOperation);
-    EXPECT_THROW(MemorySegmentMapped(mapped_file).setNamedAddress("test", 0),
-                 isc::InvalidOperation);
+                 allocate(DEFAULT_INITIAL_SIZE * 2), MemorySegmentError);
+    EXPECT_THROW(MemorySegmentMapped(mapped_file).setNamedAddress("test",
+                                                                  NULL),
+                 MemorySegmentError);
     EXPECT_THROW(MemorySegmentMapped(mapped_file).clearNamedAddress("test"),
-                 isc::InvalidOperation);
+                 MemorySegmentError);
     EXPECT_THROW(MemorySegmentMapped(mapped_file).shrinkToFit(),
-                 isc::InvalidOperation);
+                 MemorySegmentError);
 
     void* ptr = segment_->allocate(sizeof(uint32_t));
     segment_->setNamedAddress("test address", ptr);
@@ -300,7 +469,7 @@ TEST_F(MemorySegmentMappedTest, violateReadOnly) {
     }
 
     EXPECT_THROW(MemorySegmentMapped(mapped_file).deallocate(ptr, 4),
-                 isc::InvalidOperation);
+                 MemorySegmentError);
 }
 
 TEST_F(MemorySegmentMappedTest, getCheckSum) {



More information about the bind10-changes mailing list