summaryrefslogtreecommitdiffstats
path: root/src/lib/util/memory_segment.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/memory_segment.h')
-rw-r--r--src/lib/util/memory_segment.h333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/lib/util/memory_segment.h b/src/lib/util/memory_segment.h
new file mode 100644
index 0000000..c9ae97f
--- /dev/null
+++ b/src/lib/util/memory_segment.h
@@ -0,0 +1,333 @@
+// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef MEMORY_SEGMENT_H
+#define MEMORY_SEGMENT_H
+
+#include <exceptions/exceptions.h>
+
+#include <utility>
+
+#include <stdlib.h>
+
+namespace isc {
+namespace util {
+
+/// \brief Exception that can be thrown when constructing a MemorySegment
+/// object.
+class MemorySegmentOpenError : public Exception {
+public:
+ MemorySegmentOpenError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// \brief Exception that is thrown, when allocating space in a MemorySegment
+/// results in growing the underlying segment.
+///
+/// See MemorySegment::allocate() for details.
+class MemorySegmentGrown : public Exception {
+public:
+ MemorySegmentGrown(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// \brief General error that can be thrown by a MemorySegment
+/// implementation.
+class MemorySegmentError : public Exception {
+public:
+ MemorySegmentError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// \brief Memory Segment Class
+///
+/// This class specifies an interface for allocating memory segments.
+/// It's intended to provide a unified interface, whether the underlying
+/// memory is local to a specific process or is sharable by multiple
+/// processes.
+///
+/// This is an abstract class and a real implementation such as
+/// MemorySegmentLocal should be used in code.
+class MemorySegment {
+public:
+ /// \brief Destructor
+ virtual ~MemorySegment() {}
+
+ /// \brief Allocate/acquire a fragment of memory.
+ ///
+ /// The source of the memory is dependent on the implementation used.
+ ///
+ /// Depending on the implementation details, it may have to grow the
+ /// internal memory segment (again, in an implementation dependent way)
+ /// to allocate the required size of memory. In that case the
+ /// implementation must grow the internal segment sufficiently so the
+ /// next call to allocate() for the same size will succeed, and throw
+ /// a \c MemorySegmentGrown exception (not really allocating the memory
+ /// yet).
+ ///
+ /// An application that uses this memory segment abstraction to allocate
+ /// memory should expect this exception, and should normally catch it
+ /// at an appropriate layer (which may be immediately after a call to
+ /// \c allocate() or a bit higher layer). It should interpret the
+ /// exception as any raw address that belongs to the segment may have
+ /// 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 that takes a memory segment
+ /// // and calls allocate() on it multiple times. create()
+ /// // provides an exception guarantee 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 on a per-call basis rather than as a set
+ /// of calls like in 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
+ /// space for the requested size and has grown internally.
+ /// \throw MemorySegmentError An attempt was made to allocate
+ /// storage on a read-only memory segment.
+ ///
+ /// \param size The size of the memory requested in bytes.
+ /// \return Returns pointer to the memory allocated.
+ virtual void* allocate(size_t size) = 0;
+
+ /// \brief Free/release a segment of memory.
+ ///
+ /// This method may throw <code>isc::OutOfRange</code> if \c size is
+ /// not equal to the originally allocated size. \c size could be
+ /// used by some implementations such as a slice allocator, where
+ /// freeing memory also requires the size to be specified. We also
+ /// 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
+ /// should be equal to the number of bytes originally allocated.
+ virtual void deallocate(void* ptr, size_t size) = 0;
+
+ /// \brief Check if all allocated memory was deallocated.
+ ///
+ /// \return Returns <code>true</code> if all allocated memory (including
+ /// names associated by memory addresses by \c setNamedAddress()) was
+ /// deallocated, <code>false</code> otherwise.
+ virtual bool allMemoryDeallocated() const = 0;
+
+ /// \brief Associate specified address in the segment with a given name.
+ ///
+ /// This method establishes an association between the given name and
+ /// the address in an implementation specific way. The stored address
+ /// is retrieved by the name later by calling \c getNamedAddress().
+ /// If the underlying memory segment is sharable by multiple processes,
+ /// the implementation must ensure the portability of the association;
+ /// if a process gives an address in the shared segment a name, another
+ /// process that shares the same segment should be able to retrieve the
+ /// corresponding address by that name (in such cases the real address
+ /// may be different between these two processes).
+ ///
+ /// Some names are reserved for internal use by this class. If such
+ /// a name is passed to this method, an \c isc::InvalidParameter
+ /// exception will be thrown. See \c validateName() method for details.
+ ///
+ /// \c addr must be 0 (NULL) or an address that belongs to this segment.
+ /// The latter case means it must be the return value of a previous call
+ /// to \c allocate(). The actual implementation is encouraged to detect
+ /// violation of this restriction and signal it with an exception, but
+ /// it's not an API requirement. It's generally the caller's
+ /// responsibility to meet the restriction. 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 NamedAddressResult(true, 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.
+ ///
+ /// While normally unexpected, it's possible that available space in the
+ /// segment is not sufficient to allocate a space (if not already exist)
+ /// for the specified name in the segment. In that case, if possible, the
+ /// implementation should try to grow the internal segment and retry
+ /// establishing the association. The implementation should throw
+ /// std::bad_alloc if even reasonable attempts of retry still fail.
+ ///
+ /// This method should normally return false, but if the internal segment
+ /// had to grow to store the given name, it must return true. The
+ /// 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, empty ("") or begins with
+ /// an underscore ('_').
+ /// \throw MemorySegmentError Failure of implementation specific
+ /// validation.
+ ///
+ /// \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).
+ 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.
+ validateName(name);
+ 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.
+ ///
+ /// This method returns the memory address in the segment corresponding
+ /// to the specified \c name. The name and address must have been
+ /// associated by a prior call to \c setNameAddress(). If no address
+ /// associated with the given name is found, it returns NULL.
+ ///
+ /// Some names are reserved for internal use by this class. If such
+ /// a name is passed to this method, an \c isc::InvalidParameter
+ /// exception will be thrown. See \c validateName() method for details.
+ ///
+ /// This method should generally be considered exception free, but there
+ /// can be a small chance it throws, depending on the internal
+ /// implementation (e.g., if it converts the name to std::string), so the
+ /// API doesn't guarantee that property. In general, if this method
+ /// throws it should be considered a fatal condition.
+ ///
+ /// \throw InvalidParameter name is NULL, empty ("") or begins with
+ /// an underscore ('_').
+ ///
+ /// \param name A C string of which the segment memory address is to be
+ /// returned. Must not be NULL.
+ /// \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) const {
+ // This public method implements common validation. The actual
+ // work specific to the derived segment is delegated to the
+ // corresponding protected method.
+ validateName(name);
+ return (getNamedAddressImpl(name));
+ }
+
+ /// \brief Delete a name previously associated with a segment address.
+ ///
+ /// This method deletes the association of the given \c name to
+ /// a corresponding segment address previously established by
+ /// \c setNamedAddress(). If there is no association for the given name
+ /// this method returns false; otherwise it returns true.
+ ///
+ /// Some names are reserved for internal use by this class. If such
+ /// a name is passed to this method, an \c isc::InvalidParameter
+ /// exception will be thrown. See \c validateName() method for details.
+ ///
+ /// See \c getNamedAddress() about exception consideration.
+ ///
+ /// \throw InvalidParameter name is NULL, empty ("") or begins with
+ /// an underscore ('_').
+ /// \throw MemorySegmentError Failure of implementation specific
+ /// validation.
+ ///
+ /// \param name A C string of which the segment memory address is to be
+ /// 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.
+ validateName(name);
+ return (clearNamedAddressImpl(name));
+ }
+
+private:
+ /// \brief Validate the passed name.
+ ///
+ /// This method validates the passed name (for name/address pairs)
+ /// and throws \c InvalidParameter if the name fails
+ /// validation. Otherwise, it does nothing.
+ ///
+ /// \throw InvalidParameter name is NULL, empty ("") or begins with
+ /// an underscore ('_').
+ static void validateName(const char* name) {
+ if (!name) {
+ isc_throw(InvalidParameter, "NULL is invalid for a name.");
+ } else if (*name == '\0') {
+ isc_throw(InvalidParameter, "Empty names are invalid.");
+ } else if (*name == '_') {
+ isc_throw(InvalidParameter,
+ "Names beginning with '_' are reserved for "
+ "internal use only.");
+ }
+ }
+
+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 NamedAddressResult getNamedAddressImpl(const char* name) const = 0;
+
+ /// \brief Implementation of clearNamedAddress beyond common validation.
+ virtual bool clearNamedAddressImpl(const char* name) = 0;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // MEMORY_SEGMENT_H
+
+// Local Variables:
+// mode: c++
+// End: