Commit adee8c93 authored by Francis Dupont's avatar Francis Dupont

[trac3614] removed no longer used --with-shared-memory and dependencies

parent adae5408
847. [build] fdupont
Removed unused configuration option --with-shared-memory
and associated files and variables.
(Trac #3614, git ...)
846. [bug] fdupont
Fixed subdir-objects warnings from recent versions of autotools,
e.g., on Apple OSX.
......
......@@ -1041,31 +1041,6 @@ 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" -a "$want_dns" = "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 by rerunning this script with --without-shared-memory; 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])
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)
if test "$BOOST_OFFSET_PTR_OLD" = "yes" -a "$use_shared_memory" = "yes" -a "$want_dns" = "yes"; then
AC_MSG_ERROR([You're trying to compile against boost older than 1.48 with
shared memory. Older versions of boost have a bug which causes segfaults in
offset_ptr implementation when compiled by GCC with optimisations enabled.
See ticket no. 3025 for details.
Either update boost to newer version or use --without-shared-memory.
Note that most users likely don't need shared memory support.
])
fi
# Add some default CPP flags needed for Boost, identified by the AX macro.
CPPFLAGS="$CPPFLAGS $CPPFLAGS_BOOST_THREADCONF"
......
......@@ -23,20 +23,9 @@ 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 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.
dnl BOOST_STATIC_ASSERT_WOULDFAIL set to "yes" if BOOST_STATIC_ASSERT would
dnl cause build error; otherwise set to "no"
dnl BOOST_OFFSET_PTR_OLD set to "yes" if the version of boost is older than
dnl 1.48. Older versions of boost have a bug which
dnl causes segfaults in offset_ptr implementation when
dnl compiled by GCC with optimisations enabled.
dnl See ticket no. 3025 for details.
AC_DEFUN([AX_BOOST_FOR_KEA], [
AC_LANG_SAVE
AC_LANG([C++])
......@@ -119,56 +108,9 @@ if test "X$GXX" = "Xyes"; then
BOOST_NUMERIC_CAST_WOULDFAIL=yes])
CXXFLAGS="$CXXFLAGS_SAVED"
AC_MSG_CHECKING([Boost rbtree is old])
AC_TRY_COMPILE([
#include <boost/version.hpp>
#if BOOST_VERSION < 104800
#error Too old
#endif
],,[AC_MSG_RESULT(no)
BOOST_OFFSET_PTR_OLD=no
],[AC_MSG_RESULT(yes)
BOOST_OFFSET_PTR_OLD=yes])
else
# This doesn't matter for non-g++
BOOST_NUMERIC_CAST_WOULDFAIL=no
BOOST_OFFSET_PTR_OLD=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 $CLANG_CXXFLAGS -Wall -Wextra -Werror"
try_flags="$try_flags -Wno-error"
fi
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 $CLANG_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
# BOOST_STATIC_ASSERT in versions below Boost 1.54.0 is known to result
......
......@@ -7,18 +7,6 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(KEA_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 = libkea-util.la
libkea_util_la_SOURCES = csv_file.h csv_file.cc
......@@ -29,9 +17,6 @@ libkea_util_la_SOURCES += buffer.h io_utilities.h
libkea_util_la_SOURCES += time_utilities.h time_utilities.cc
libkea_util_la_SOURCES += memory_segment.h
libkea_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc
if USE_SHARED_MEMORY
libkea_util_la_SOURCES += memory_segment_mapped.h memory_segment_mapped.cc
endif
libkea_util_la_SOURCES += optional_value.h
libkea_util_la_SOURCES += range_utilities.h
libkea_util_la_SOURCES += signal_set.cc signal_set.h
......
// 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 <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>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <cassert>
#include <string>
#include <new>
#include <stdint.h>
// boost::interprocess namespace is big and can cause unexpected import
// (e.g., it has "read_only"), so it's safer to be specific for shortcuts.
using boost::interprocess::basic_managed_mapped_file;
using boost::interprocess::rbtree_best_fit;
using boost::interprocess::null_mutex_family;
using boost::interprocess::iset_index;
using boost::interprocess::create_only_t;
using boost::interprocess::create_only;
using boost::interprocess::open_or_create_t;
using boost::interprocess::open_or_create;
using boost::interprocess::open_read_only;
using boost::interprocess::open_only;
using boost::interprocess::offset_ptr;
namespace isc {
namespace util {
namespace { // unnamed namespace
const char* const RESERVED_NAMED_ADDRESS_STORAGE_NAME =
"_RESERVED_NAMED_ADDRESS_STORAGE";
} // end of unnamed namespace
// 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
// there's at most one writer process or concurrent writing the shared memory
// segment is protected at a higher level. Using the null mutex is mainly for
// eliminating unnecessary dependency; the default version would require
// (probably depending on the system) Pthread library that is actually not
// needed and could cause various build time troubles.
typedef basic_managed_mapped_file<char,
rbtree_best_fit<null_mutex_family>,
iset_index> BaseSegment;
struct MemorySegmentMapped::Impl {
// Constructor for create-only (and read-write) mode. this case is
// tricky because we want to remove any existing file but we also want
// to detect possible conflict with other readers or writers using
// file lock.
Impl(const std::string& filename, create_only_t, size_t initial_size) :
read_only_(false), filename_(filename)
{
try {
// First, try opening it in boost create_only mode; it fails if
// the file exists (among other reasons).
base_sgmt_.reset(new BaseSegment(create_only, filename.c_str(),
initial_size));
} catch (const boost::interprocess::interprocess_exception& ex) {
// We assume this is because the file exists; otherwise creating
// file_lock would fail with interprocess_exception, and that's
// what we want here (we wouldn't be able to create a segment
// anyway).
lock_.reset(new boost::interprocess::file_lock(filename.c_str()));
// Confirm there's no other reader or writer, and then release
// the lock before we remove the file; there's a chance of race
// here, but this check doesn't intend to guarantee 100% safety
// and so it should be okay.
checkWriter();
lock_.reset();
// now remove the file (if it happens to have been delete, this
// will be no-op), then re-open it with create_only. this time
// it should succeed, and if it fails again, that's fatal for this
// constructor.
boost::interprocess::file_mapping::remove(filename.c_str());
base_sgmt_.reset(new BaseSegment(create_only, filename.c_str(),
initial_size));
}
// confirm there's no other user and there won't either.
lock_.reset(new boost::interprocess::file_lock(filename.c_str()));
checkWriter();
reserveMemory();
}
// 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)),
lock_(new boost::interprocess::file_lock(filename.c_str()))
{
checkWriter();
reserveMemory();
}
// 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_ ?
new BaseSegment(open_read_only, filename.c_str()) :
new BaseSegment(open_only, filename.c_str())),
lock_(new boost::interprocess::file_lock(filename.c_str()))
{
if (read_only_) {
checkReader();
} else {
checkWriter();
}
reserveMemory();
}
void reserveMemory(bool no_grow = false) {
if (!read_only_) {
// Reserve a named address for use during
// setNamedAddress(). Though this will almost always succeed
// on the first try during construction, it may require
// multiple attempts later during a call from
// allMemoryDeallocated() when the segment has been in use
// for a while.
while (true) {
const offset_ptr<void>* reserved_storage =
base_sgmt_->find_or_construct<offset_ptr<void> >(
RESERVED_NAMED_ADDRESS_STORAGE_NAME, std::nothrow)();
if (reserved_storage) {
break;
}
assert(!no_grow);
growSegment();
}
}
}
void freeReservedMemory() {
if (!read_only_) {
const bool deleted = base_sgmt_->destroy<offset_ptr<void> >
(RESERVED_NAMED_ADDRESS_STORAGE_NAME);
assert(deleted);
}
}
// Internal helper to grow the underlying mapped segment.
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 > prev_size);
const bool grown = BaseSegment::grow(filename_.c_str(),
new_size - prev_size);
// Remap the file, whether or not grow() succeeded. this should
// normally succeed(*), but it's not 100% guaranteed. We abort
// if it fails (see the method description in the header file).
// (*) Although it's not formally documented, the implementation
// of grow() seems to provide strong guarantee, i.e, if it fails
// the underlying file can be used with the previous size.
try {
base_sgmt_.reset(new BaseSegment(open_only, filename_.c_str()));
} catch (...) {
abort();
}
if (!grown) {
throw std::bad_alloc();
}
}
// remember if the segment is opened read-only or not
const bool read_only_;
// mapped file; remember it in case we need to grow it.
const std::string filename_;
// actual Boost implementation of mapped segment.
boost::scoped_ptr<BaseSegment> base_sgmt_;
private:
// helper methods and member to detect any reader-writer conflict at
// the time of construction using an advisory file lock. The lock will
// be held throughout the lifetime of the object and will be released
// automatically.
void checkReader() {
if (!lock_->try_lock_sharable()) {
isc_throw(MemorySegmentOpenError,
"mapped memory segment can't be opened as read-only "
"with a writer process");
}
}
void checkWriter() {
if (!lock_->try_lock()) {
isc_throw(MemorySegmentOpenError,
"mapped memory segment can't be opened as read-write "
"with other reader or writer processes");
}
}
boost::scoped_ptr<boost::interprocess::file_lock> lock_;
};
MemorySegmentMapped::MemorySegmentMapped(const std::string& filename) :
impl_(NULL)
{
try {
impl_ = new Impl(filename, true);
} catch (const boost::interprocess::interprocess_exception& ex) {
isc_throw(MemorySegmentOpenError,
"failed to open mapped memory segment for " << filename
<< ": " << ex.what());
}
}
MemorySegmentMapped::MemorySegmentMapped(const std::string& filename,
OpenMode mode, size_t initial_size) :
impl_(NULL)
{
try {
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:
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,
"failed to open mapped memory segment for " << filename
<< ": " << ex.what());
}
}
MemorySegmentMapped::~MemorySegmentMapped() {
if (impl_->base_sgmt_ && !impl_->read_only_) {
impl_->freeReservedMemory();
impl_->base_sgmt_->flush(); // note: this is exception free
}
delete impl_;
}
void*
MemorySegmentMapped::allocate(size_t size) {
if (impl_->read_only_) {
isc_throw(MemorySegmentError, "allocate attempt on read-only segment");
}
// We explicitly check the free memory size; it appears
// managed_mapped_file::allocate() could incorrectly return a seemingly
// valid pointer for some very large requested size.
if (impl_->base_sgmt_->get_free_memory() >= size) {
void* ptr = impl_->base_sgmt_->allocate(size, std::nothrow);
if (ptr) {
return (ptr);
}
}
// Grow the mapped segment doubling the size until we have sufficient
// free memory in the revised segment for the requested size.
do {
impl_->growSegment();
} while (impl_->base_sgmt_->get_free_memory() < size);
isc_throw(MemorySegmentGrown, "mapped memory segment grown, size: "
<< impl_->base_sgmt_->get_size() << ", free size: "
<< impl_->base_sgmt_->get_free_memory());
}
void
MemorySegmentMapped::deallocate(void* ptr, size_t) {
if (impl_->read_only_) {
isc_throw(MemorySegmentError,
"deallocate attempt on read-only segment");
}
// 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;
}
impl_->base_sgmt_->deallocate(ptr);
}
bool
MemorySegmentMapped::allMemoryDeallocated() const {
// This method is not technically const, but it reserves the
// const-ness property. In case of exceptions, we abort here. (See
// ticket #2850 for additional commentary.)
try {
impl_->freeReservedMemory();
const bool result = impl_->base_sgmt_->all_memory_deallocated();
// reserveMemory() should succeed now as the memory was already
// allocated, so we set no_grow to true.
impl_->reserveMemory(true);
return (result);
} catch (...) {
abort();
}
}
MemorySegment::NamedAddressResult
MemorySegmentMapped::getNamedAddressImpl(const char* name) const {
offset_ptr<void>* storage =
impl_->base_sgmt_->find<offset_ptr<void> >(name).first;
if (storage) {
return (NamedAddressResult(true, storage->get()));
}
return (NamedAddressResult(false, NULL));
}
bool
MemorySegmentMapped::setNamedAddressImpl(const char* name, void* addr) {
if (impl_->read_only_) {
isc_throw(MemorySegmentError, "setNamedAddress on read-only segment");
}
if (addr && !impl_->base_sgmt_->belongs_to_segment(addr)) {
isc_throw(MemorySegmentError, "address is out of segment: " << addr);
}
// Temporarily save the passed addr into pre-allocated offset_ptr in
// case there are any relocations caused by allocations.
offset_ptr<void>* reserved_storage =
impl_->base_sgmt_->find<offset_ptr<void> >(
RESERVED_NAMED_ADDRESS_STORAGE_NAME).first;
assert(reserved_storage);
*reserved_storage = addr;
bool grown = false;
while (true) {
offset_ptr<void>* storage =
impl_->base_sgmt_->find_or_construct<offset_ptr<void> >(
name, std::nothrow)();
if (storage) {
// Move the address from saved offset_ptr into the
// newly-allocated storage.
reserved_storage =
impl_->base_sgmt_->find<offset_ptr<void> >(
RESERVED_NAMED_ADDRESS_STORAGE_NAME).first;
assert(reserved_storage);
*storage = *reserved_storage;
return (grown);
}
impl_->growSegment();
grown = true;
}
}
bool
MemorySegmentMapped::clearNamedAddressImpl(const char* name) {
if (impl_->read_only_) {
isc_throw(MemorySegmentError,
"clearNamedAddress on read-only segment");
}
return (impl_->base_sgmt_->destroy<offset_ptr<void> >(name));
}
void
MemorySegmentMapped::shrinkToFit() {
if (impl_->read_only_) {
isc_throw(MemorySegmentError, "shrinkToFit on read-only segment");
}
// It appears an assertion failure is triggered within Boost if the size
// 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;
}
// First, unmap the underlying file.
impl_->base_sgmt_->flush();
impl_->base_sgmt_.reset();
BaseSegment::shrink_to_fit(impl_->filename_.c_str());
try {
// 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. Note that this is different from the case where
// reset() after grow() fails. While the same argument can apply
// in theory, it should be less likely that other methods will be
// called after shrinkToFit() (and the destructor can still be called
// safely), so we give the application an opportunity to handle the
// case as gracefully as possible.
impl_->base_sgmt_.reset(
new BaseSegment(open_only, impl_->filename_.c_str()));
} catch (const boost::interprocess::interprocess_exception& ex) {
isc_throw(MemorySegmentError,
"remap after shrink failed; segment is now unusable");
}
}
size_t
MemorySegmentMapped::getSize() const {
return (impl_->base_sgmt_->get_size());
}
size_t
MemorySegmentMapped::getCheckSum() const {
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();
size_t sum = 0;
for (const uint8_t* cp = cp_begin; cp < cp_end; cp += pagesize) {
sum += *cp;
}
return (sum);
}
} // namespace util
} // namespace isc
// 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 MEMORY_SEGMENT_MAPPED_H
#define MEMORY_SEGMENT_MAPPED_H
#include <util/memory_segment.h>
#include <boost/noncopyable.hpp>
#include <string>
namespace isc {
namespace util {
/// \brief Mapped-file based Memory Segment class.
///
/// This implementation of \c MemorySegment uses a concrete file to be mapped
/// into memory. Multiple processes can share the same mapped memory image.
///
/// This class provides two operation modes: read-only and read-write.
/// A \c MemorySegmentMapped object in the read-only mode cannot modify the
/// mapped memory image or other internal maintenance data of the object;
/// In the read-write mode the object can allocate or deallocate memory
/// from the mapped image, and the owner process can change the content.
///
/// Multiple processes can open multiple segments for the same file in
/// read-only mode at the same time. But there shouldn't be more than
/// one process that opens segments for the same file in read-write mode
/// at the same time. Likewise, if one process opens a segment for a
/// file in read-write mode, there shouldn't be any other process that
/// opens a segment for the file in read-only mode. If one or more
/// processes open segments for a file in read-only mode, there
/// shouldn't be any other process that opens a segment for the file in
/// read-write mode. This class tries to detect any violation of this
/// restriction, but this does not intend to provide 100% safety. It's
/// generally the user's responsibility to ensure this condition.
///
/// The same restriction applies within the single process, whether
/// multi-threaded or not: a process shouldn't open read-only and read-write
/// (or multiple read-write) segments for the same file. The violation
/// detection mentioned above may or may not work in such cases due to
/// limitation of the underlying API. It's completely user's responsibility
/// to prevent this from happening. A single process may open multiple
/// segments in read-only mode for the same file, but that shouldn't be
/// necessary in practice; since it's read-only there wouldn't be a reason
/// to have a redundant copy.
class MemorySegmentMapped : boost::noncopyable, public MemorySegment {
public:
/// \brief The default value of the mapped file size when newly created.
///
/// Its value, 32KB, is an arbitrary choice, but considered to be
/// 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.