From 931dc3f8839094aceb293f90149fdc81841f6587 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Oct 2011 05:35:36 +0100 Subject: [PATCH] draft: automatically invoke an attached ctor/dtor functor --- src/proc/engine/buffer-metadata.hpp | 132 ++++++++++++------ .../proc/engine/buffer-metadata-test.cpp | 61 ++++++-- tests/components/proc/engine/testframe.hpp | 19 +++ 3 files changed, 157 insertions(+), 55 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 500d1994b..f86d1cdaf 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -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 diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index b32da07ab..85818ba6f 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -37,13 +37,16 @@ //#include #include #include +#include //#include //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 + TY& + accessAs (metadata::Entry& entry) + { + TY* ptr = reinterpret_cast (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 (f0) = testData(1); + accessAs (f1) = testData(2); + accessAs (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 (r0), randStr(SIZE_B - 1).c_str(), SIZE_B); + strncpy (& accessAs (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; diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index ba71d6646..d9e7b7b20 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -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) {