Library: implement optional invocation of destructors
This is the first draft, implementing the invocation explicitly through a trampoline function. While it seems to work, the formulation can probably be simplified....
This commit is contained in:
parent
31f8664725
commit
71d5851701
5 changed files with 221 additions and 44 deletions
|
|
@ -70,8 +70,32 @@ namespace lib {
|
|||
{
|
||||
Alloc::destroy (heap, static_cast<std::byte *> (storage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Special allocator-policy for lib::LinkedElements
|
||||
* - does not allow to allocate new elements
|
||||
* - can hook up elements allocated elsewhere
|
||||
* - ensure the destructor of all elements is invoked
|
||||
*/
|
||||
struct PolicyInvokeDtor
|
||||
: lib::linked_elements::NoOwnership
|
||||
{
|
||||
/**
|
||||
* while this policy doesn't take ownership,
|
||||
* it ensures the destructor is invoked
|
||||
*/
|
||||
template<class X>
|
||||
void
|
||||
destroy (X* elm)
|
||||
{
|
||||
REQUIRE (elm);
|
||||
elm->~X();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An _overlay view_ for the AllocationCluster to add functionality
|
||||
* for adding / clearing extents and registering optional deleter functions.
|
||||
|
|
@ -91,9 +115,15 @@ namespace lib {
|
|||
struct Destructor
|
||||
{
|
||||
Destructor* next;
|
||||
///////////////////////////////////////////////OOO store deleter function here
|
||||
DtorInvoker dtor;
|
||||
|
||||
~Destructor()
|
||||
{
|
||||
if (dtor)
|
||||
(*dtor) (this);
|
||||
}
|
||||
};
|
||||
using Destructors = lib::LinkedElements<Destructor>;
|
||||
using Destructors = lib::LinkedElements<Destructor, PolicyInvokeDtor>;
|
||||
|
||||
struct Extent
|
||||
: util::NonCopyable
|
||||
|
|
@ -112,7 +142,7 @@ namespace lib {
|
|||
|
||||
ManagementView view_;
|
||||
|
||||
StorageManager() = delete; ///< @warning used as _overlay view_ only, never created
|
||||
StorageManager() = delete; ///< @warning used as _overlay view_ only, never created
|
||||
|
||||
public:
|
||||
static StorageManager&
|
||||
|
|
@ -135,6 +165,20 @@ namespace lib {
|
|||
view_.extents.clear();
|
||||
}
|
||||
|
||||
void
|
||||
addDestructor (void* dtor)
|
||||
{
|
||||
auto& destructor = * static_cast<Destructor*> (dtor);
|
||||
getCurrentBlockStart()->dtors.push (destructor);
|
||||
}
|
||||
|
||||
void
|
||||
discardLastDestructor()
|
||||
{
|
||||
getCurrentBlockStart()->dtors.pop();
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
determineExtentCnt() ///< @warning finalises current block
|
||||
{
|
||||
|
|
@ -152,12 +196,13 @@ namespace lib {
|
|||
static auto determineStorageSize (AllocationCluster const&);
|
||||
|
||||
private:
|
||||
byte*
|
||||
Extent*
|
||||
getCurrentBlockStart() const
|
||||
{
|
||||
return static_cast<byte*>(view_.storage.pos)
|
||||
+ view_.storage.rest
|
||||
- EXTENT_SIZ;
|
||||
void* pos = static_cast<byte*>(view_.storage.pos)
|
||||
+ view_.storage.rest
|
||||
- EXTENT_SIZ;
|
||||
return static_cast<Extent*> (pos);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -221,7 +266,22 @@ namespace lib {
|
|||
ENSURE (allocRequest + OVERHEAD <= EXTENT_SIZ);
|
||||
StorageManager::access(*this).addBlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
AllocationCluster::registerDestructor (void* dtor)
|
||||
{
|
||||
StorageManager::access(*this).addDestructor (dtor);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AllocationCluster::discardLastDestructor()
|
||||
{
|
||||
StorageManager::access(*this).discardLastDestructor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* === diagnostics helpers === */
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
|
@ -125,11 +127,10 @@ namespace lib {
|
|||
|
||||
|
||||
template<class TY, typename...ARGS>
|
||||
TY&
|
||||
create (ARGS&& ...args)
|
||||
{
|
||||
return * new(allot<TY>()) TY (std::forward<ARGS> (args)...);
|
||||
}
|
||||
TY& create (ARGS&& ...);
|
||||
|
||||
template<class TY, typename...ARGS>
|
||||
TY& createDisposable (ARGS&& ...);
|
||||
|
||||
template<typename X>
|
||||
Allocator<X>
|
||||
|
|
@ -167,7 +168,14 @@ namespace lib {
|
|||
return static_cast<X*> (allotMemory (cnt * sizeof(X), alignof(X)));
|
||||
}
|
||||
|
||||
typedef void (*DtorInvoker) (void*);
|
||||
|
||||
template<typename X>
|
||||
void* allotWithDeleter();
|
||||
|
||||
void expandStorage (size_t);
|
||||
void registerDestructor (void*);
|
||||
void discardLastDestructor();
|
||||
bool _is_within_limits (size_t,size_t);
|
||||
|
||||
friend class test::AllocationCluster_test;
|
||||
|
|
@ -179,6 +187,75 @@ namespace lib {
|
|||
|
||||
//-----implementation-details------------------------
|
||||
|
||||
template<class TY, typename...ARGS>
|
||||
TY&
|
||||
AllocationCluster::createDisposable (ARGS&& ...args)
|
||||
{
|
||||
return * new(allot<TY>()) TY (std::forward<ARGS> (args)...);
|
||||
}
|
||||
|
||||
template<class TY, typename...ARGS>
|
||||
TY&
|
||||
AllocationCluster::create (ARGS&& ...args)
|
||||
{
|
||||
if constexpr (std::is_trivial_v<TY>)
|
||||
return createDisposable<TY> (std::forward<ARGS> (args)...);
|
||||
|
||||
void* storage = allotWithDeleter<TY>();
|
||||
try {
|
||||
return * new(storage) TY (std::forward<ARGS> (args)...);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
discardLastDestructor();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a storage arrangement with a callback to invoke the destructor.
|
||||
* @remark the purpose of AllocationCluster is to avoid deallocation of individual objects;
|
||||
* thus the position and type of allocated payload objects is discarded. However,
|
||||
* sometimes it is desirable to ensure invocation of object destructors; in this case,
|
||||
* a linked list of destructor callbacks is hooked up in the storage extent. These
|
||||
* callback records are always allocated directly before the actual payload object,
|
||||
* and use a special per-type trampoline function to invoke the destructor, passing
|
||||
* a properly adjusted self-pointer.
|
||||
*/
|
||||
template<typename X>
|
||||
void*
|
||||
AllocationCluster::allotWithDeleter()
|
||||
{
|
||||
/**
|
||||
* Memory layout frame to place a payload object
|
||||
* and store a destructor callback as intrusive linked list.
|
||||
* @note this object is never constructed, but it is used to
|
||||
* reinterpret the StorageManager::Destructor record,
|
||||
* causing invocation of the destructor of the payload object,
|
||||
* which is always placed immediately behind.
|
||||
*/
|
||||
struct TypedDtorInvoker
|
||||
{
|
||||
void* next;
|
||||
DtorInvoker dtor;
|
||||
X payload;
|
||||
|
||||
/** trampoline function: invoke the destructor of the payload type */
|
||||
static void
|
||||
invokePayloadDtor (void* self)
|
||||
{
|
||||
REQUIRE (self);
|
||||
TypedDtorInvoker* instance = static_cast<TypedDtorInvoker*> (self);
|
||||
instance->payload.~X();
|
||||
}
|
||||
};
|
||||
|
||||
TypedDtorInvoker* allocation = allot<TypedDtorInvoker>();
|
||||
allocation->dtor = &TypedDtorInvoker::invokePayloadDtor;
|
||||
registerDestructor (allocation);
|
||||
return & allocation->payload;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*LIB_ALLOCATION_CLUSTER_H*/
|
||||
|
|
|
|||
|
|
@ -323,12 +323,20 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
// template< class TY =N>
|
||||
// TY& //_________________________________________
|
||||
// emplace () ///< add object of type TY, using 0-arg ctor
|
||||
// {
|
||||
// return push (ALO::template create<TY>());
|
||||
// }
|
||||
/** extract the top-most element, if any
|
||||
* @warning gives up ownership; if this list manages ownership,
|
||||
* then the caller is responsible for deallocating the removed entry
|
||||
* @return pointer to the removed element
|
||||
*/
|
||||
N*
|
||||
pop()
|
||||
{
|
||||
N* elm = head_;
|
||||
if (head_)
|
||||
head_ = head_->next;
|
||||
return elm;
|
||||
}
|
||||
|
||||
|
||||
/** prepend object of type TY, forwarding ctor args */
|
||||
template<class TY =N, typename...ARGS>
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ namespace test {
|
|||
verifyInternals()
|
||||
{
|
||||
CHECK (0==checksum);
|
||||
long markSum;
|
||||
{
|
||||
AllocationCluster clu;
|
||||
// no allocation happened yet
|
||||
|
|
@ -265,34 +266,17 @@ namespace test {
|
|||
CHECK (slot(0) == 0);
|
||||
|
||||
// deliberately fill up the first extent completely
|
||||
SHOW_EXPR(size_t(currBlock()))
|
||||
SHOW_EXPR(size_t(clu.storage_.pos))
|
||||
SHOW_EXPR(clu.storage_.rest)
|
||||
SHOW_EXPR(posOffset())
|
||||
for (uint i=clu.storage_.rest; i>0; --i)
|
||||
clu.create<uchar> (i);
|
||||
SHOW_EXPR(size_t(currBlock()))
|
||||
SHOW_EXPR(size_t(clu.storage_.pos))
|
||||
SHOW_EXPR(clu.storage_.rest)
|
||||
SHOW_EXPR(posOffset())
|
||||
SHOW_EXPR(slot(0))
|
||||
CHECK (clu.storage_.rest == 0);
|
||||
CHECK (clu.storage_.rest == 0); // no space left in current extent
|
||||
CHECK (posOffset() == BLOCKSIZ);
|
||||
SHOW_EXPR(clu.numBytes())
|
||||
CHECK (clu.numBytes() == BLOCKSIZ - 2*sizeof(void*));
|
||||
CHECK (clu.numBytes() == BLOCKSIZ - 2*sizeof(void*)); // now using all the rest behind the admin »slots«
|
||||
CHECK (clu.numExtents() == 1);
|
||||
CHECK (slot(0) == 0);
|
||||
CHECK (blk == currBlock()); // but still in the initial extent
|
||||
|
||||
// trigger overflow and allocation of second extent
|
||||
char& c2 = clu.create<char> ('U');
|
||||
SHOW_EXPR(size_t(currBlock()))
|
||||
SHOW_EXPR(size_t(clu.storage_.pos))
|
||||
SHOW_EXPR(clu.storage_.rest)
|
||||
SHOW_EXPR(posOffset())
|
||||
SHOW_EXPR(slot(0))
|
||||
SHOW_EXPR(clu.numBytes())
|
||||
SHOW_EXPR(clu.numExtents())
|
||||
CHECK (blk != currBlock()); // allocation moved to a new extent
|
||||
CHECK (getAddr(c2) == currBlock() + 2*sizeof(void*)); // c2 resides immediately after the two administrative »slots«
|
||||
CHECK (clu.storage_.rest == BLOCKSIZ - posOffset());
|
||||
|
|
@ -304,8 +288,33 @@ SHOW_EXPR(clu.numExtents())
|
|||
CHECK (c1 == 'X');
|
||||
CHECK (c2 == 'U');
|
||||
CHECK (i3 == 42);
|
||||
|
||||
// allocate a "disposable" object (dtor will not be called)
|
||||
SHOW_EXPR(clu.numBytes())
|
||||
SHOW_EXPR(posOffset())
|
||||
size_t pp = posOffset();
|
||||
auto& o1 = clu.createDisposable<Dummy<2>> (4);
|
||||
CHECK (o1.getID() == 4);
|
||||
SHOW_EXPR(clu.numBytes())
|
||||
SHOW_EXPR(posOffset())
|
||||
SHOW_EXPR(checksum)
|
||||
markSum = checksum;
|
||||
CHECK (checksum == 4+4);
|
||||
CHECK (alignof(Dummy<2>) == alignof(char));
|
||||
CHECK (posOffset() - pp == sizeof(Dummy<2>));
|
||||
|
||||
// allocate a similar object,
|
||||
// but this time also enrolling the destructor
|
||||
pp = posOffset();
|
||||
auto& o2 = clu.create<Dummy<2>> (8);
|
||||
CHECK (o2.getID() == 8);
|
||||
CHECK (checksum == markSum + 8+8);
|
||||
SHOW_EXPR(clu.numBytes())
|
||||
SHOW_EXPR(posOffset())
|
||||
SHOW_EXPR(checksum)
|
||||
}
|
||||
CHECK (0==checksum);
|
||||
SHOW_EXPR(checksum)
|
||||
CHECK (checksum == markSum);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81768,8 +81768,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<arrowlink COLOR="#faf8b2" DESTINATION="ID_1028046936" ENDARROW="Default" ENDINCLINATION="102;3;" ID="Arrow_ID_1495010693" STARTARROW="None" STARTINCLINATION="118;4;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716130029614" ID="ID_229036433" MODIFIED="1716130547847" TEXT="automatischer Destruktor-Aufruf">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1716130029614" ID="ID_229036433" MODIFIED="1716604728822" TEXT="automatischer Destruktor-Aufruf">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1716130041704" ID="ID_1028046936" MODIFIED="1716130101251" TEXT="kann getriggert werden von StorageManager::Destructor">
|
||||
<linktarget COLOR="#faf8b2" DESTINATION="ID_1028046936" ENDARROW="Default" ENDINCLINATION="102;3;" ID="Arrow_ID_1495010693" SOURCE="ID_350853857" STARTARROW="None" STARTINCLINATION="118;4;"/>
|
||||
</node>
|
||||
|
|
@ -81815,12 +81815,25 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716130607500" ID="ID_641247354" MODIFIED="1716130643729" TEXT="brauche somit nur noch einen Trampolin-Pointer">
|
||||
<node COLOR="#338800" CREATED="1716130607500" ID="ID_641247354" MODIFIED="1716604674352" TEXT="brauche somit nur noch einen Trampolin-Pointer">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1716604651319" ID="ID_398123666" MODIFIED="1716604673317" TEXT="muß sicherstellen, daß Destroctor-Record und Objekt beieinander liegen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1716606608657" ID="ID_1834443608" MODIFIED="1716606616344" TEXT="geht das nicht einfacher und sauberer?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1716606623571" ID="ID_1928898649" MODIFIED="1716606636617" TEXT="im Grunde ist das hier ganz gewöhnlicher Polymorphismus"/>
|
||||
<node CREATED="1716606637660" ID="ID_1428982691" MODIFIED="1716606656903" TEXT="nur „zu fuß“ implementiert">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716606714899" ID="ID_631957038" MODIFIED="1716606741395" TEXT="der Deleter-Invoker könnte durch eine VTable ersetzt werden">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1716130724127" ID="ID_727448469" MODIFIED="1716133312180" TEXT="low-level-Test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1716130730820" ID="ID_539013579" MODIFIED="1716130744964" TEXT="ich mache hier gefährliche Sachen">
|
||||
|
|
@ -81902,8 +81915,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715783068923" ID="ID_34296369" MODIFIED="1715783077530" TEXT="Tests für Error-Handling zurückbauen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716130852555" ID="ID_1309770620" MODIFIED="1716130866602" TEXT="Test für interne low-level-Mechanismen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1716130852555" ID="ID_1309770620" MODIFIED="1716605447662" TEXT="Test für interne low-level-Mechanismen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716130874040" ID="ID_253325898" MODIFIED="1716133346231" TEXT="verifyInternals">
|
||||
<arrowlink COLOR="#4173be" DESTINATION="ID_1783945506" ENDARROW="Default" ENDINCLINATION="204;17;" ID="Arrow_ID_1547348126" STARTARROW="None" STARTINCLINATION="292;-22;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
@ -81926,6 +81939,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node COLOR="#338800" CREATED="1716552190584" ID="ID_1042988047" MODIFIED="1716593224739" TEXT="einen Überlauf provozieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1716605425287" ID="ID_1793345065" MODIFIED="1716605442774" TEXT="Objekt-Allokation mit registriertem Destruktor">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1716605457035" ID="ID_442879470" MODIFIED="1716605483931" TEXT="sieht gut aus...">
|
||||
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1716605469169" ID="ID_680488739" MODIFIED="1716605487862" TEXT="zusätzliche Storage belegt"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1716605475561" ID="ID_43688892" MODIFIED="1716605487863" TEXT="Destruktor wird aufgerufen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716605489686" ID="ID_838033504" MODIFIED="1716605500558" TEXT="Verzeigerung explizit verifizieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716552266490" ID="ID_34007851" MODIFIED="1716552275535" TEXT="weiteren Überlauf mit Alignment">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue