genode/repos/base/include/base/allocator.h

324 lines
9.0 KiB
C
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Generic allocator interface
* \author Norman Feske
* \date 2006-04-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2011-12-22 16:19:25 +01:00
*/
#ifndef _INCLUDE__BASE__ALLOCATOR_H_
#define _INCLUDE__BASE__ALLOCATOR_H_
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
#include <util/interface.h>
2011-12-22 16:19:25 +01:00
#include <base/stdint.h>
#include <base/exception.h>
#include <base/quota_guard.h>
2011-12-22 16:19:25 +01:00
namespace Genode {
struct Deallocator;
struct Allocator;
struct Range_allocator;
2011-12-22 16:19:25 +01:00
template <typename T, typename DEALLOC> void destroy(DEALLOC && dealloc, T *obj);
2011-12-22 16:19:25 +01:00
}
/**
* Deallocator interface
*/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
struct Genode::Deallocator : Interface
{
/**
* Free block a previously allocated block
*/
virtual void free(void *addr, 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 method 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 = 0;
};
struct Genode::Allocator : Deallocator
{
/**
* Exception type
*/
typedef Out_of_ram Out_of_memory;
/**
* 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
*
* \throw Out_of_ram
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
* \throw Out_of_caps
*
* \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 method, we prevent the
* compiler from warning us about "dereferencing type-punned
* pointer will break strict-aliasing rules".
*
* \throw Out_of_ram
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
* \throw Out_of_caps
*/
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 total amount of backing store consumed by the allocator
*/
virtual size_t consumed() const { return 0; }
/**
* Return meta-data overhead per block
*/
virtual size_t overhead(size_t size) const = 0;
/**
* Allocate block and signal error as an exception
*
* \param size block size to allocate
*
* \throw Out_of_ram
Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=<amount>' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: <default-route> <service name="PD" unscoped_label="timer"> <parent diag="yes"/> </service> ... </default-route> For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '<report>' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398
2017-05-08 21:35:43 +02:00
* \throw Out_of_caps
*
* \return pointer to the new block
*/
void *alloc(size_t size)
{
void *result = 0;
if (!alloc(size, &result))
throw Out_of_memory();
return result;
}
};
struct Genode::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 ok() const { return value == OK; }
bool error() const { return !ok(); }
/*
* \deprecated use 'ok' and 'error' instead
*/
bool is_ok() const { return ok(); }
bool is_error() const { return error(); }
};
/**
* 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, addr_t from=0, addr_t to = ~0UL) = 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 *)' method
* 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) override = 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() const = 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) const = 0;
};
void *operator new (__SIZE_TYPE__, Genode::Allocator *);
void *operator new [] (__SIZE_TYPE__, Genode::Allocator *);
void *operator new (__SIZE_TYPE__, Genode::Allocator &);
void *operator new [] (__SIZE_TYPE__, Genode::Allocator &);
2011-12-22 16:19:25 +01:00
/**
* Delete operator invoked when an exception occurs during the construction of
* a dynamically allocated object
*
* When an exception occurs during the construction of a dynamically allocated
* object, the C++ standard devises the automatic invocation of the global
* operator delete. When passing an allocator as argument to the new operator
* (the typical case for Genode), the compiler magically calls the operator
* delete taking the allocator type as second argument. This is how we end up
* here.
*
* There is one problem though: We get the pointer of the to-be-deleted object
* but not its size. But Genode's 'Allocator' interface requires the object
* size to be passed as argument to 'Allocator::free()'.
*
* Even though in the general case, we cannot assume all 'Allocator'
* implementations to remember the size of each allocated object, the commonly
* used 'Heap', 'Sliced_heap', 'Allocator_avl', and 'Slab' do so and ignore the
* size argument. When using either of those allocators, we are fine. Otherwise
* we print a warning and pass the zero size argument anyway.
*
* :Warning: Never use an allocator that depends on the size argument of the
* 'free()' method for the allocation of objects that may throw exceptions
* at their construction time!
*/
void operator delete (void *, Genode::Deallocator *);
void operator delete (void *, Genode::Deallocator &);
/**
* The compiler looks for a delete operator signature matching the one of the
* new operator. If none is found, it omits (silently!) the generation of
* implicitly delete calls, which are needed when an exception is thrown within
* the constructor of the object.
*/
inline void operator delete (void *ptr, Genode::Allocator *a) {
operator delete (ptr, static_cast<Genode::Deallocator *>(a)); }
inline void operator delete (void *ptr, Genode::Allocator &a) {
operator delete (ptr, *static_cast<Genode::Deallocator *>(&a)); }
/**
* Destroy object
*
* For destroying an object, we need to specify the allocator that was used
* by the object. Because we cannot pass the allocator directly to the
* delete expression, we mimic the expression by using this template
* function. The function explicitly calls the object destructor and
* operator delete afterwards.
*
* For details see https://github.com/genodelabs/genode/issues/1030.
*
* \param T implicit object type
*
* \param dealloc reference or pointer to allocator from which the object
* was allocated
* \param obj object to destroy
*/
template <typename T, typename DEALLOC>
void Genode::destroy(DEALLOC && dealloc, T *obj)
{
if (!obj)
return;
/* call destructors */
obj->~T();
/*
* Free memory at the allocator
*
* We have to use the delete operator instead of just calling
* 'dealloc.free' because the 'obj' pointer might not always point to the
* begin of the allocated block:
*
* If 'T' is the base class of another class 'A', 'obj' may refer
* to an instance of 'A'. In particular when 'A' used multiple inheritance
* with 'T' not being the first inhertited class, the pointer to the actual
* object differs from 'obj'.
*
* Given the pointer to the base class 'T', however, the delete operator is
* magically (by the means of the information found in the vtable of 'T')
* able to determine the actual pointer to the instance of 'A' and passes
* this pointer to 'free'.
*/
operator delete (obj, dealloc);
}
2011-12-22 16:19:25 +01:00
#endif /* _INCLUDE__BASE__ALLOCATOR_H_ */