draft: automatically invoke an attached ctor/dtor functor
This commit is contained in:
parent
ccd130966b
commit
931dc3f883
3 changed files with 157 additions and 55 deletions
|
|
@ -395,10 +395,10 @@ namespace engine {
|
|||
: public Key
|
||||
{
|
||||
BufferState state_;
|
||||
const void* buffer_;
|
||||
void* buffer_;
|
||||
|
||||
protected:
|
||||
Entry (Key const& parent, const void* bufferPtr =0)
|
||||
Entry (Key const& parent, void* bufferPtr =0)
|
||||
: Key (Key::forEntry (parent, bufferPtr))
|
||||
, state_(bufferPtr? LOCKED:NIL)
|
||||
, buffer_(bufferPtr)
|
||||
|
|
@ -435,8 +435,8 @@ namespace engine {
|
|||
return state_;
|
||||
}
|
||||
|
||||
const void*
|
||||
access() const
|
||||
void*
|
||||
access()
|
||||
{
|
||||
__must_not_be_NIL();
|
||||
__must_not_be_FREE();
|
||||
|
|
@ -458,7 +458,8 @@ namespace engine {
|
|||
||(state_ == BLOCKED && newState == FREE))
|
||||
{
|
||||
// allowed transition
|
||||
if (newState == FREE) buffer_ = 0;
|
||||
if (newState == FREE)
|
||||
invokeEmbeddedDtor_and_clear();
|
||||
state_ = newState;
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -466,6 +467,29 @@ namespace engine {
|
|||
throw error::Fatal ("Invalid buffer state encountered.");
|
||||
}
|
||||
|
||||
protected:
|
||||
/** @internal maybe invoke a registered TypeHandler's
|
||||
* constructor function, which typically builds some
|
||||
* content object into the buffer by placement new. */
|
||||
void
|
||||
invokeEmbeddedCtor()
|
||||
{
|
||||
REQUIRE (buffer_);
|
||||
if (nontrivial (instanceFunc_))
|
||||
instanceFunc_.createAttached (buffer_);
|
||||
}
|
||||
|
||||
/** @internal maybe invoke a registered TypeHandler's
|
||||
* destructor function, which typically clears up some
|
||||
* content object living within the buffer */
|
||||
void
|
||||
invokeEmbeddedDtor_and_clear()
|
||||
{
|
||||
REQUIRE (buffer_);
|
||||
if (nontrivial (instanceFunc_))
|
||||
instanceFunc_.destroyAttached (buffer_);
|
||||
buffer_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
|
|
@ -668,9 +692,9 @@ namespace engine {
|
|||
* @note might create/register a new Entry as a side-effect
|
||||
*/
|
||||
Key const&
|
||||
key (Key const& parentKey, const void* concreteBuffer)
|
||||
key (Key const& parentKey, void* concreteBuffer)
|
||||
{
|
||||
return get (parentKey,concreteBuffer);
|
||||
return lock (parentKey,concreteBuffer);
|
||||
}
|
||||
|
||||
/** core operation to access or create a concrete buffer metadata entry.
|
||||
|
|
@ -678,6 +702,11 @@ namespace engine {
|
|||
* which denotes a buffer type, and the concrete buffer address. If yet
|
||||
* unknown, a new concrete buffer metadata Entry is created and initialised
|
||||
* to LOCKED state. Otherwise just the existing Entry is fetched.
|
||||
* @note this function really \em activates the buffer.
|
||||
* In case the type (Key) involves a TypeHandler (functor),
|
||||
* its constructor function will be invoked, if actually a new
|
||||
* entry gets created. Typically this mechanism will be used
|
||||
* to placement-create an object into the buffer.
|
||||
* @param parentKey a key describing the \em type of the buffer
|
||||
* @param concreteBuffer storage pointer, must not be NULL
|
||||
* @param onlyNew disallow fetching an existing entry
|
||||
|
|
@ -691,7 +720,7 @@ namespace engine {
|
|||
* buffer is released or re-used later.
|
||||
*/
|
||||
Entry&
|
||||
get (Key const& parentKey, const void* concreteBuffer, bool onlyNew =false)
|
||||
lock (Key const& parentKey, void* concreteBuffer, bool onlyNew =false)
|
||||
{
|
||||
if (!concreteBuffer)
|
||||
throw error::Invalid ("Attempt to lock a slot for a NULL buffer"
|
||||
|
|
@ -705,7 +734,7 @@ namespace engine {
|
|||
, error::LUMIERA_ERROR_LIFECYCLE );
|
||||
|
||||
if (!existing)
|
||||
return table_.store (newEntry);
|
||||
return store_and_lock (newEntry); // actual creation
|
||||
else
|
||||
return *existing;
|
||||
}
|
||||
|
|
@ -743,10 +772,45 @@ namespace engine {
|
|||
}
|
||||
|
||||
|
||||
/* == memory management == */
|
||||
|
||||
Entry& markLocked (Key const& parentKey, const void* buffer);
|
||||
void release (HashVal key);
|
||||
/* == memory management operations == */
|
||||
|
||||
/** combine the type (Key) with a concrete buffer,
|
||||
* thereby marking this buffer as locked. Store a concrete
|
||||
* metadata Entry to account for this fact. This might include
|
||||
* invoking a constructor function, in case the type (Key)
|
||||
* defines a (nontrivial) TypeHandler.
|
||||
* @throw error::Fatal when locking a NULL buffer
|
||||
* @throw exceptions which might be raised by a TypeHandler's
|
||||
* constructor function. In this case, the Entry remains
|
||||
* created, but is marked as FREE
|
||||
*/
|
||||
Entry&
|
||||
markLocked (Key const& parentKey, void* buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
|
||||
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
||||
return this->lock(parentKey, buffer, true); // force creation of a new entry
|
||||
}
|
||||
|
||||
/** purge the bare metadata Entry from the metadata tables.
|
||||
* @throw error::Logic if the entry isn't marked FREE already
|
||||
*/
|
||||
void
|
||||
release (HashVal key)
|
||||
{
|
||||
Entry* entry = table_.fetch (key);
|
||||
if (!entry) return;
|
||||
if (entry && (FREE != entry->state()))
|
||||
throw error::Logic ("Attempt to release a buffer still in use"
|
||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
table_.remove (key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -765,40 +829,28 @@ namespace engine {
|
|||
if (isKnown (key)) return;
|
||||
table_.store (Entry (key, NULL));
|
||||
}
|
||||
|
||||
Entry&
|
||||
store_and_lock (Entry const& metadata)
|
||||
{
|
||||
Entry& newEntry = table_.store (metadata);
|
||||
try
|
||||
{
|
||||
newEntry.invokeEmbeddedCtor();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
newEntry.mark(FREE);
|
||||
throw;
|
||||
}
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
inline BufferMetadata::Entry&
|
||||
BufferMetadata::markLocked (Key const& parentKey, const void* buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
|
||||
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
||||
return this->get (parentKey, buffer, true); // force creation of a new entry
|
||||
}
|
||||
|
||||
inline void
|
||||
BufferMetadata::release (HashVal key)
|
||||
{
|
||||
Entry* entry = table_.fetch (key);
|
||||
if (!entry) return;
|
||||
if (entry && (FREE != entry->state()))
|
||||
throw error::Logic ("Attempt to release a buffer still in use"
|
||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
table_.remove (key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -37,13 +37,16 @@
|
|||
//#include <boost/format.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
//#include <iostream>
|
||||
|
||||
//using boost::format;
|
||||
//using std::string;
|
||||
//using std::cout;
|
||||
//using util::for_each;
|
||||
using std::strncpy;
|
||||
using boost::scoped_ptr;
|
||||
using lib::test::randStr;
|
||||
using util::isnil;
|
||||
using util::isSameObject;
|
||||
|
||||
|
|
@ -66,13 +69,24 @@ namespace test {
|
|||
const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
|
||||
const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
|
||||
|
||||
const HashVal JUST_SOMETHING = 123;
|
||||
const void* const SOME_POINTER = &JUST_SOMETHING;
|
||||
HashVal JUST_SOMETHING = 123;
|
||||
void* const SOME_POINTER = &JUST_SOMETHING;
|
||||
// const uint TEST_SIZE = 1024*1024;
|
||||
// const uint TEST_ELMS = 20;
|
||||
|
||||
|
||||
}
|
||||
|
||||
template<typename TY>
|
||||
TY&
|
||||
accessAs (metadata::Entry& entry)
|
||||
{
|
||||
TY* ptr = reinterpret_cast<TY*> (entry.access());
|
||||
ASSERT (ptr);
|
||||
return *ptr;
|
||||
}
|
||||
}//(End) Test fixture and helpers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
|
|
@ -231,10 +245,6 @@ namespace test {
|
|||
CHECK (LOCKED == r0.state());
|
||||
CHECK (LOCKED == r1.state());
|
||||
|
||||
// for the TestFrame buffers, additionally we'd have to create/attach an object
|
||||
attachTestFrame.createAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically??
|
||||
attachTestFrame.createAttached (frames+1); //////////////////////////TODO: answer: yes. Metadata is exactly the entity which has all necessary information
|
||||
attachTestFrame.createAttached (frames+2);
|
||||
|
||||
CHECK (f0.access() == frames+0);
|
||||
CHECK (f1.access() == frames+1);
|
||||
|
|
@ -242,6 +252,11 @@ namespace test {
|
|||
CHECK (r0.access() == rawbuf+0);
|
||||
CHECK (r1.access() == rawbuf+1);
|
||||
|
||||
TestFrame defaultFrame;
|
||||
CHECK (defaultFrame == f0.access());
|
||||
CHECK (defaultFrame == f1.access());
|
||||
CHECK (defaultFrame == f2.access());
|
||||
|
||||
// at that point, we'd return BuffHandles to the client
|
||||
HashVal handle_f0(f0);
|
||||
HashVal handle_f1(f1);
|
||||
|
|
@ -249,19 +264,31 @@ namespace test {
|
|||
HashVal handle_r0(r0);
|
||||
HashVal handle_r1(r1);
|
||||
|
||||
// client uses the buffers
|
||||
// client uses the buffers---------------------(Start)
|
||||
accessAs<TestFrame> (f0) = testData(1);
|
||||
accessAs<TestFrame> (f1) = testData(2);
|
||||
accessAs<TestFrame> (f2) = testData(3);
|
||||
|
||||
//////////////////TODO: access the storage through the metadata-key
|
||||
//////////////////TODO: to a state transition on the metadata
|
||||
f0.mark(FREE);
|
||||
CHECK (TestFrame::isAlive (f0.access()));
|
||||
CHECK (TestFrame::isAlive (f1.access()));
|
||||
CHECK (TestFrame::isAlive (f2.access()));
|
||||
|
||||
strncpy (& accessAs<char> (r0), randStr(SIZE_B - 1).c_str(), SIZE_B);
|
||||
strncpy (& accessAs<char> (r1), randStr(SIZE_B - 1).c_str(), SIZE_B);
|
||||
|
||||
// client might trigger some state transitions
|
||||
f0.mark(EMITTED);
|
||||
f1.mark(EMITTED);
|
||||
f1.mark(BLOCKED);
|
||||
// client uses the buffers---------------------(End)
|
||||
|
||||
|
||||
f0.mark(FREE); // note: this implicitly invoked the embedded dtor
|
||||
f1.mark(FREE);
|
||||
f2.mark(FREE);
|
||||
r0.mark(FREE);
|
||||
r1.mark(FREE);
|
||||
|
||||
attachTestFrame.destroyAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically??
|
||||
attachTestFrame.destroyAttached (frames+1);
|
||||
attachTestFrame.destroyAttached (frames+2);
|
||||
|
||||
meta_->release(handle_f0);
|
||||
meta_->release(handle_f1);
|
||||
|
|
@ -269,6 +296,10 @@ namespace test {
|
|||
meta_->release(handle_r0);
|
||||
meta_->release(handle_r1);
|
||||
|
||||
CHECK (TestFrame::isDead (&frames[0]));
|
||||
CHECK (TestFrame::isDead (&frames[1]));
|
||||
CHECK (TestFrame::isDead (&frames[2]));
|
||||
|
||||
// manual cleanup of test allocations
|
||||
delete[] frames;
|
||||
delete[] rawbuf;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,25 @@ namespace test {
|
|||
|
||||
public:
|
||||
|
||||
/** Helper to verify a given memory location holds
|
||||
* an active TestFrame instance (created, not yet destroyed)
|
||||
* @return true if the TestFrame datastructure is intact and
|
||||
* marked as still alive.
|
||||
*/
|
||||
static bool
|
||||
isAlive (void* memLocation)
|
||||
{
|
||||
UNIMPLEMENTED ("access memory as TestFrame and check internal accounting");
|
||||
}
|
||||
|
||||
/** Helper to verify a given memory location holds
|
||||
* an already destroyed TestFrame instance */
|
||||
static bool
|
||||
isDead (void* memLocation)
|
||||
{
|
||||
UNIMPLEMENTED ("access memory as TestFrame and verify dtor invocation");
|
||||
}
|
||||
|
||||
bool
|
||||
operator== (void* memLocation)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue