base: New Genode::Deallocator interface

Splitting the new Genode::Deallocator interface from the former
Genode::Allocator interface enables us to restrict the accessible
operations for code that is only supposed to release memory, but not
perform any allocations.

Additionally, this patch introduces variants of the 'new' operator
that takes a reference (as opposed to a pointer) to a Genode::Allocator
as argument.
This commit is contained in:
Norman Feske 2014-01-17 11:03:36 +01:00 committed by Christian Helmuth
parent 99979e09ed
commit 6ec36350d6
2 changed files with 184 additions and 181 deletions

View File

@ -19,186 +19,185 @@
namespace Genode {
class Allocator
struct Deallocator
{
public:
/**
* Free block a previously allocated block
*/
virtual void free(void *addr, size_t size) = 0;
/*********************
** Exception types **
*********************/
class Out_of_memory : public Exception { };
/**
* Destructor
*/
virtual ~Allocator() { }
/**
* Allocate block
*
* \param size block size to allocate
* \param out_addr resulting pointer to the new block,
* undefined in the error case
* \return true on success
*/
virtual bool alloc(size_t size, void **out_addr) = 0;
/**
* Allocate typed block
*
* This template allocates a typed block returned as a pointer to
* a non-void type. By providing this function, we prevent the
* compiler from warning us about "dereferencing type-punned
* pointer will break strict-aliasing rules".
*/
template <typename T> bool alloc(size_t size, T **out_addr)
{
void *addr = 0;
bool ret = alloc(size, &addr);
*out_addr = (T *)addr;
return ret;
}
/**
* Free block a previously allocated block
*/
virtual void free(void *addr, size_t size) = 0;
/**
* Return total amount of backing store consumed by the allocator
*/
virtual size_t consumed() { return 0; }
/**
* Return meta-data overhead per block
*/
virtual size_t overhead(size_t size) = 0;
/**
* Return true if the size argument of 'free' is required
*
* The generic 'Allocator' interface requires the caller of 'free'
* to supply a valid size argument but not all implementations make
* use of this argument. If this function returns false, it is safe
* to call 'free' with an invalid size.
*
* Allocators that rely on the size argument must not be used for
* constructing objects whose constructors may throw exceptions.
* See the documentation of 'operator delete(void *, Allocator *)'
* below for more details.
*/
virtual bool need_size_for_free() const { return true; }
/***************************
** Convenience functions **
***************************/
/**
* Allocate block and signal error as an exception
*
* \param size block size to allocate
* \return pointer to the new block
* \throw Out_of_memory
*/
void *alloc(size_t size)
{
void *result = 0;
if (!alloc(size, &result))
throw Out_of_memory();
return result;
}
/**
* Return true if the size argument of 'free' is required
*
* The generic 'Allocator' interface requires the caller of 'free'
* to supply a valid size argument but not all implementations make
* use of this argument. If this function returns false, it is safe
* to call 'free' with an invalid size.
*
* Allocators that rely on the size argument must not be used for
* constructing objects whose constructors may throw exceptions.
* See the documentation of 'operator delete(void *, Allocator *)'
* below for more details.
*/
virtual bool need_size_for_free() const { return true; }
};
class Range_allocator : public Allocator
struct Allocator : Deallocator
{
public:
/**
* Exception type
*/
class Out_of_memory : public Exception { };
/**
* Destructor
*/
virtual ~Range_allocator() { }
/**
* Destructor
*/
virtual ~Allocator() { }
/**
* Add free address range to allocator
*/
virtual int add_range(addr_t base, size_t size) = 0;
/**
* Allocate block
*
* \param size block size to allocate
* \param out_addr resulting pointer to the new block,
* undefined in the error case
* \return true on success
*/
virtual bool alloc(size_t size, void **out_addr) = 0;
/**
* Remove address range from allocator
*/
virtual int remove_range(addr_t base, size_t size) = 0;
/**
* Allocate typed block
*
* This template allocates a typed block returned as a pointer to
* a non-void type. By providing this function, we prevent the
* compiler from warning us about "dereferencing type-punned
* pointer will break strict-aliasing rules".
*/
template <typename T> bool alloc(size_t size, T **out_addr)
{
void *addr = 0;
bool ret = alloc(size, &addr);
*out_addr = (T *)addr;
return ret;
}
/**
* Return value of allocation functons
*
* 'OK' on success, or
* 'OUT_OF_METADATA' if meta-data allocation failed, or
* 'RANGE_CONFLICT' if no fitting address range is found
*/
struct Alloc_return
{
enum Value { OK = 0, OUT_OF_METADATA = -1, RANGE_CONFLICT = -2 };
Value const value;
Alloc_return(Value value) : value(value) { }
/**
* Return total amount of backing store consumed by the allocator
*/
virtual size_t consumed() { return 0; }
bool is_ok() const { return value == OK; }
bool is_error() const { return !is_ok(); }
};
/**
* Return meta-data overhead per block
*/
virtual size_t overhead(size_t size) = 0;
/**
* Allocate block
*
* \param size size of new block
* \param out_addr start address of new block,
* undefined in the error case
* \param align alignment of new block specified
* as the power of two
*/
virtual Alloc_return alloc_aligned(size_t size, void **out_addr, int align = 0) = 0;
/**
* Allocate block at address
*
* \param size size of new block
* \param addr desired address of block
*
* \return 'ALLOC_OK' on success, or
* 'OUT_OF_METADATA' if meta-data allocation failed, or
* 'RANGE_CONFLICT' if specified range is occupied
*/
virtual Alloc_return alloc_addr(size_t size, addr_t addr) = 0;
/***************************
** Convenience functions **
***************************/
/**
* Free a previously allocated block
*
* NOTE: We have to declare the 'Allocator::free(void *)' function
* here as well to make the compiler happy. Otherwise the C++
* overload resolution would not find 'Allocator::free(void *)'.
*/
virtual void free(void *addr) = 0;
virtual void free(void *addr, size_t size) = 0;
/**
* Allocate block and signal error as an exception
*
* \param size block size to allocate
* \return pointer to the new block
* \throw Out_of_memory
*/
void *alloc(size_t size)
{
void *result = 0;
if (!alloc(size, &result))
throw Out_of_memory();
/**
* Return the sum of available memory
*
* Note that the returned value is not neccessarily allocatable
* because the memory may be fragmented.
*/
virtual size_t avail() = 0;
return result;
}
};
/**
* Check if address is inside an allocated block
*
* \param addr address to check
*
* \return true if address is inside an allocated block, false
* otherwise
*/
virtual bool valid_addr(addr_t addr) = 0;
struct Range_allocator : Allocator
{
/**
* Destructor
*/
virtual ~Range_allocator() { }
/**
* Add free address range to allocator
*/
virtual int add_range(addr_t base, size_t size) = 0;
/**
* Remove address range from allocator
*/
virtual int remove_range(addr_t base, size_t size) = 0;
/**
* Return value of allocation functons
*
* 'OK' on success, or
* 'OUT_OF_METADATA' if meta-data allocation failed, or
* 'RANGE_CONFLICT' if no fitting address range is found
*/
struct Alloc_return
{
enum Value { OK = 0, OUT_OF_METADATA = -1, RANGE_CONFLICT = -2 };
Value const value;
Alloc_return(Value value) : value(value) { }
bool is_ok() const { return value == OK; }
bool is_error() const { return !is_ok(); }
};
/**
* Allocate block
*
* \param size size of new block
* \param out_addr start address of new block,
* undefined in the error case
* \param align alignment of new block specified
* as the power of two
*/
virtual Alloc_return alloc_aligned(size_t size, void **out_addr, int align = 0) = 0;
/**
* Allocate block at address
*
* \param size size of new block
* \param addr desired address of block
*
* \return 'ALLOC_OK' on success, or
* 'OUT_OF_METADATA' if meta-data allocation failed, or
* 'RANGE_CONFLICT' if specified range is occupied
*/
virtual Alloc_return alloc_addr(size_t size, addr_t addr) = 0;
/**
* Free a previously allocated block
*
* NOTE: We have to declare the 'Allocator::free(void *)' function
* here as well to make the compiler happy. Otherwise the C++
* overload resolution would not find 'Allocator::free(void *)'.
*/
virtual void free(void *addr) = 0;
virtual void free(void *addr, size_t size) = 0;
/**
* Return the sum of available memory
*
* Note that the returned value is not neccessarily allocatable
* because the memory may be fragmented.
*/
virtual size_t avail() = 0;
/**
* Check if address is inside an allocated block
*
* \param addr address to check
*
* \return true if address is inside an allocated block, false
* otherwise
*/
virtual bool valid_addr(addr_t addr) = 0;
};
@ -216,7 +215,7 @@ namespace Genode {
* \param obj object to destroy
*/
template <typename T>
void destroy(Allocator *alloc, T *obj)
void destroy(Deallocator *dealloc, T *obj)
{
if (!obj)
return;
@ -225,12 +224,15 @@ namespace Genode {
obj->~T();
/* free memory at the allocator */
alloc->free(obj, sizeof(T));
dealloc->free(obj, sizeof(T));
}
}
void *operator new (Genode::size_t size, Genode::Allocator *allocator);
void *operator new [] (Genode::size_t size, Genode::Allocator *allocator);
void *operator new (Genode::size_t, Genode::Allocator *);
void *operator new [] (Genode::size_t, Genode::Allocator *);
void *operator new (Genode::size_t, Genode::Allocator &);
void *operator new [] (Genode::size_t, Genode::Allocator &);
/**

View File

@ -14,25 +14,26 @@
#include <base/printf.h>
#include <base/allocator.h>
using Genode::size_t;
using Genode::Allocator;
void *operator new(Genode::size_t size, Genode::Allocator *allocator)
static void *try_alloc(Allocator *alloc, size_t size)
{
if (!allocator)
throw Genode::Allocator::Out_of_memory();
if (!alloc)
throw Allocator::Out_of_memory();
return allocator->alloc(size);
}
void *operator new [] (Genode::size_t size, Genode::Allocator *allocator)
{
if (!allocator)
throw Genode::Allocator::Out_of_memory();
return allocator->alloc(size);
return alloc->alloc(size);
}
void operator delete (void *ptr, Genode::Allocator *alloc)
void *operator new (size_t s, Allocator *a) { return try_alloc(a, s); }
void *operator new [] (size_t s, Allocator *a) { return try_alloc(a, s); }
void *operator new (size_t s, Allocator &a) { return a.alloc(s); }
void *operator new [] (size_t s, Allocator &a) { return a.alloc(s); }
void operator delete (void *ptr, Allocator *alloc)
{
/*
* Warn on the attempt to use an allocator that relies on the size