%PDF- %PDF-
| Direktori : /backups/router/usr/local/include/kea/util/ |
| Current File : //backups/router/usr/local/include/kea/util/memory_segment.h |
// Copyright (C) 2012-2024 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