diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 484450a10..c33c59ea7 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -235,6 +235,15 @@ namespace engine { return newKey; } + void + useTypeHandlerFrom (Key const& ref) + { + if (nontrivial(this->instanceFunc_)) + throw error::Logic ("unable to supersede an already attached TypeHandler" + , LUMIERA_ERROR_LIFECYCLE); + instanceFunc_ = ref.instanceFunc_; + } + LocalKey const& localKey() const { return specifics_;} size_t storageSize() const { return storageSize_; } @@ -346,6 +355,16 @@ namespace engine { return mark (LOCKED); } + Entry& + invalidate (bool invokeDtor =true) + { + if (buffer_ && invokeDtor) + invokeEmbeddedDtor_and_clear(); + buffer_ = 0; + state_ = FREE; + return *this; + } + protected: /** @internal maybe invoke a registered TypeHandler's @@ -712,11 +731,19 @@ namespace engine { { Entry* entry = table_.fetch (key); if (!entry) return; - if (entry && (FREE != entry->state())) + + ASSERT (entry && (key == HashVal(*entry))); + release (*entry); + } + + void + release (Entry const& entry) + { + if (FREE != entry.state()) throw error::Logic ("Attempt to release a buffer still in use" , error::LUMIERA_ERROR_LIFECYCLE); - table_.remove (key); + table_.remove (HashVal(entry)); } diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index 449b41370..1303d1458 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -89,7 +89,7 @@ namespace engine { * to be returned to the client as result of the #lockBuffer call. * Performs the necessary metadata state transition leading from an * abstract buffer type to a metadata::Entry corresponding to an - * actual buffer, which is locked for exclusive use by one client. + * actual buffer, which is locked for exclusive use by one client. */ BuffHandle BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) @@ -178,8 +178,9 @@ namespace engine { BufferProvider::releaseBuffer (BuffHandle const& handle) try { metadata::Entry& metaEntry = meta_->get (handle.entryID()); + metaEntry.mark(FREE); // might invoke embedded dtor function detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); - metaEntry.mark(FREE); + meta_->release (metaEntry); } ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider") @@ -198,7 +199,15 @@ namespace engine { void BufferProvider::attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference) { - UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); + metadata::Entry& metaEntry = meta_->get (target.entryID()); + metadata::Entry& refEntry = meta_->get (reference); + REQUIRE (refEntry.isTypeKey()); + REQUIRE (!metaEntry.isTypeKey()); + if (!metaEntry.isLocked()) + throw error::Logic ("unable to attach an object because buffer isn't locked for use" + , LUMIERA_ERROR_LIFECYCLE); + + metaEntry.useTypeHandlerFrom (refEntry); // EX_STRONG } @@ -211,10 +220,14 @@ namespace engine { */ void BufferProvider::emergencyCleanup (BuffHandle const& target, bool invokeDtor) - { - UNIMPLEMENTED ("emergency cleanup"); + try { + metadata::Entry& metaEntry = meta_->get (target.entryID()); + metaEntry.invalidate (invokeDtor); + detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); + meta_->release (metaEntry); } - + ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error") + bool @@ -222,7 +235,7 @@ namespace engine { { return isSameObject (*this, *descr.provider_); } - + @@ -267,20 +280,35 @@ namespace engine { void BuffHandle::emergencyCleanup() { - descriptor_.provider_->emergencyCleanup(*this); // EX_FREE - pBuffer_ = 0; + descriptor_.provider_->emergencyCleanup(*this); // EX_FREE + pBuffer_ = 0; } + /** Install a standard TypeHandler for an already locked buffer. + * This causes the dtor function to be invoked when releasing this buffer. + * The assumption is that client code will placement-construct an object + * into this buffer right away, and thus we're taking ownership on that object. + * @param type a reference BufferDescriptor defining an embedded TypeHandler to use + * A copy of this TypeHandler will be stored into the local metadata for + * this buffer only, not altering the basic buffer type in any way + * @throw lifecycle error when attempting to treat an buffer not in locked state + * @throw error::Logic in case of insufficient buffer space to hold the + * intended target object + * @note EX_STRONG + */ void BuffHandle::takeOwnershipFor(BufferDescriptor const& type) { + if (!this->isValid()) + throw error::Logic ("attaching an object requires an buffer in locked state" + , LUMIERA_ERROR_LIFECYCLE); if (this->size() < type.determineBufferSize()) throw error::Logic ("insufficient buffer size to hold an instance of that type"); descriptor_.provider_->attachTypeHandler(*this, type); // EX_STRONG } - + diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 4fff36efe..37add12f1 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -131,10 +131,10 @@ namespace engine { bool was_created_by_this_provider (BufferDescriptor const&) const; }; - - - - + + + + /* === Implementation === */ /** convenience shortcut: diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp index 4f5b94b55..0b2d3a0ae 100644 --- a/src/proc/engine/buffhandle-attach.hpp +++ b/src/proc/engine/buffhandle-attach.hpp @@ -67,6 +67,7 @@ namespace engine { { \ emergencyCleanup(); /* EX_FREE */ \ pBuffer_ = 0; \ + throw; \ } /** convenience shortcut: place and maintain an object within the buffer. diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index a035f3a69..9ed842cb4 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -43,7 +43,7 @@ ** @see BufferProvider ** @see BufferProviderProtocol_test usage demonstration ** @see OutputSlot - ** @see bufftable.hpp storage for the buffer table + ** @see bufftable.hpp storage for the buffer table ** @see engine::RenderInvocation */ @@ -76,8 +76,6 @@ namespace engine { * @note this descriptor and especially the #subClassification_ is really owned * by the BufferProvider, which may use (and even change) the opaque contents * to organise the internal buffer management. - * - * @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249 */ class BufferDescriptor { @@ -108,9 +106,6 @@ namespace engine { /** * Handle for a buffer for processing data, abstracting away the actual implementation. * The real buffer pointer can be retrieved by dereferencing this smart-handle class. - * - * @todo as of 6/2011 it isn't clear how buffer handles are actually created - * and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage */ class BuffHandle : public lib::BoolCheckable diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index f00dcb8a3..98f65cf94 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -27,13 +27,6 @@ #include "proc/engine/diagnostic-buffer-provider.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" -//#include -//#include - -//using lib::ScopedPtrVect; -//using boost::scoped_array; - - namespace engine { @@ -42,20 +35,10 @@ namespace engine { lib::Singleton DiagnosticBufferProvider::diagnostics; - - - namespace { // Details of allocation and accounting - - - } // (END) Details of allocation and accounting - - - - DiagnosticBufferProvider::DiagnosticBufferProvider() - : pImpl_() //////////TODO create PImpl here + : pImpl_() { } DiagnosticBufferProvider::~DiagnosticBufferProvider() { } @@ -78,9 +61,9 @@ namespace engine { return diagnostics(); } - - - + + + TrackingHeapBlockProvider& DiagnosticBufferProvider::reset() { @@ -93,9 +76,9 @@ namespace engine { { return &implInstance == pImpl_.get(); } - - + + /* === diagnostic API === */ diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index 83bb589ca..cef2c1e0d 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" #include "lib/singleton.hpp" #include "lib/util.hpp" +#include "proc/engine/type-handler.hpp" #include "proc/engine/buffer-provider.hpp" #include @@ -98,22 +99,6 @@ namespace engine { bool all_buffers_released() const; - template - bool - object_was_attached (uint bufferID) const - { - UNIMPLEMENTED ("verify object attachment status of a specific buffer"); - } - - - template - bool - object_was_destroyed (uint bufferID) const - { - UNIMPLEMENTED ("verify object attachment status of a specific buffer"); - } - - private: diff --git a/tests/46engine.tests b/tests/46engine.tests index 07fac0119..b0de4f200 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -12,7 +12,7 @@ return: 0 END -PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test < -//#include - -//using boost::format; -//using std::string; -//using std::cout; +using util::isSameObject; using util::for_each; namespace engine{ namespace test { -// using lib::AllocationCluster; -// using mobject::session::PEffect; + using lib::test::Dummy; + using ::engine::BuffHandle; - using lumiera::error::LUMIERA_ERROR_LIFECYCLE; + using error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_LIFECYCLE; namespace { // Test fixture @@ -64,12 +60,16 @@ namespace test { } - /******************************************************************* - * @test verify the OutputSlot interface and base implementation - * by performing full data exchange cycle. This is a - * kind of "dry run" for documentation purposes, - * both the actual OutputSlot implementation - * as the client using this slot are Mocks. + /****************************************************************************** + * @test verify and demonstrate the usage cycle of data buffers for the engine + * based on the BufferProvider interface. This is kind of a "dry run" + * for documentation purposes, because the BufferProvider implementation + * used here is just a diagnostics facility, allowing to investigate + * the state of individual buffers even after "releasing" them. + * + * This test should help understanding the sequence of buffer management + * operations performed at various stages while passing an calculation job + * through the render engine. */ class BufferProviderProtocol_test : public Test { @@ -78,6 +78,8 @@ namespace test { { verifySimpleUsage(); verifyStandardCase(); + verifyObjectAttachment(); + verifyObjectAttachmentFailure(); } @@ -97,6 +99,7 @@ namespace test { TestFrame& content = buff.accessAs(); CHECK (testData(0) == content); + buff.emit(); buff.release(); CHECK (!buff.isValid()); VERIFY_ERROR (LIFECYCLE, buff.accessAs() ); @@ -104,8 +107,6 @@ namespace test { DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider); CHECK (checker.buffer_was_used (0)); CHECK (checker.buffer_was_closed (0)); - CHECK (checker.object_was_attached (0)); - CHECK (checker.object_was_destroyed (0)); CHECK (testData(0) == checker.accessMemory (0)); } @@ -146,6 +147,83 @@ namespace test { CHECK (checker.all_buffers_released()); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 } + + + void + verifyObjectAttachment() + { + BufferProvider& provider = DiagnosticBufferProvider::build(); + BufferDescriptor type_A = provider.getDescriptorFor(sizeof(TestFrame)); + BufferDescriptor type_B = provider.getDescriptorFor(sizeof(int)); + BufferDescriptor type_C = provider.getDescriptor(); + + BuffHandle handle_A = provider.lockBuffer(type_A); + BuffHandle handle_B = provider.lockBuffer(type_B); + BuffHandle handle_C = provider.lockBuffer(type_C); + + CHECK (handle_A); + CHECK (handle_B); + CHECK (handle_C); + + CHECK (sizeof(TestFrame) == handle_A.size()); + CHECK (sizeof( int ) == handle_B.size()); + CHECK (sizeof( int ) == handle_C.size()); + + TestFrame& embeddedFrame = handle_A.create(); + CHECK (isSameObject (*handle_A, embeddedFrame)); + CHECK (embeddedFrame.isAlive()); + CHECK (embeddedFrame.isSane()); + + VERIFY_ERROR (LOGIC, handle_B.create()); // too small to hold a TestFrame + VERIFY_ERROR (LIFECYCLE, handle_C.create()); // has already an attached TypeHandler (creating an int) + + handle_A.release(); + handle_B.release(); + handle_C.release(); + + CHECK (embeddedFrame.isDead()); + CHECK (embeddedFrame.isSane()); + } + + + void + verifyObjectAttachmentFailure() + { + BufferProvider& provider = DiagnosticBufferProvider::build(); + BufferDescriptor type_D = provider.getDescriptorFor(sizeof(Dummy)); + + Dummy::checksum() = 0; + BuffHandle handle_D = provider.lockBuffer(type_D); + CHECK (0 == Dummy::checksum()); // nothing created thus far + + handle_D.create(); + CHECK (0 < Dummy::checksum()); + + handle_D.release(); + CHECK (0 == Dummy::checksum()); + + BuffHandle handle_DD = provider.lockBuffer(type_D); + + CHECK (0 == Dummy::checksum()); + Dummy::activateCtorFailure(); + + CHECK (handle_DD.isValid()); + try + { + handle_DD.create(); + NOTREACHED ("Dummy ctor should fail"); + } + catch (int val) + { + CHECK (!handle_DD.isValid()); + + CHECK (0 < Dummy::checksum()); + CHECK (val == Dummy::checksum()); + } + + VERIFY_ERROR (LIFECYCLE, handle_DD.accessAs() ); + VERIFY_ERROR (LIFECYCLE, handle_DD.create() ); + } };