BufferProvider interface finished thus far.

simulated lifecycle passes unit test
This commit is contained in:
Fischlurch 2011-11-26 00:09:59 +01:00
parent 2fce2b1c8d
commit 7ba8ff432f
10 changed files with 178 additions and 81 deletions

View file

@ -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));
}

View file

@ -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
}

View file

@ -131,10 +131,10 @@ namespace engine {
bool was_created_by_this_provider (BufferDescriptor const&) const;
};
/* === Implementation === */
/** convenience shortcut:

View file

@ -67,6 +67,7 @@ namespace engine {
{ \
emergencyCleanup(); /* EX_FREE */ \
pBuffer_ = 0; \
throw; \
}
/** convenience shortcut: place and maintain an object within the buffer.

View file

@ -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<BuffHandle>

View file

@ -27,13 +27,6 @@
#include "proc/engine/diagnostic-buffer-provider.hpp"
#include "proc/engine/tracking-heap-block-provider.hpp"
//#include <boost/scoped_array.hpp>
//#include <vector>
//using lib::ScopedPtrVect;
//using boost::scoped_array;
namespace engine {
@ -42,20 +35,10 @@ namespace engine {
lib::Singleton<DiagnosticBufferProvider> 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 === */

View file

@ -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 <boost/scoped_ptr.hpp>
@ -98,22 +99,6 @@ namespace engine {
bool all_buffers_released() const;
template<typename BU>
bool
object_was_attached (uint bufferID) const
{
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
}
template<typename BU>
bool
object_was_destroyed (uint bufferID) const
{
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
}
private:

View file

@ -12,7 +12,7 @@ return: 0
END
PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END
TEST "Buffer provider diagnostics" BufferProviderProtocol_test <<END
return: 0
END

View file

@ -333,7 +333,7 @@ namespace test {
// store a set of parameter values, later to be used on invocation
args.storeTuple (
tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), rand() % 100));
tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), 10 + rand() % 90));
CHECK (!isnil (args));
cout << args << endl;

View file

@ -24,29 +24,25 @@
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/test/testdummy.hpp"
#include "lib/util-foreach.hpp"
//#include "proc/play/diagnostic-output-slot.hpp"
#include "proc/engine/testframe.hpp"
#include "proc/engine/diagnostic-buffer-provider.hpp"
#include "proc/engine/buffhandle-attach.hpp"
#include "proc/engine/bufftable.hpp"
//#include <boost/format.hpp>
//#include <iostream>
//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<TestFrame>();
CHECK (testData(0) == content);
buff.emit();
buff.release();
CHECK (!buff.isValid());
VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() );
@ -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<TestFrame> (0));
CHECK (checker.object_was_destroyed<TestFrame> (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<int>();
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<TestFrame>();
CHECK (isSameObject (*handle_A, embeddedFrame));
CHECK (embeddedFrame.isAlive());
CHECK (embeddedFrame.isSane());
VERIFY_ERROR (LOGIC, handle_B.create<TestFrame>()); // too small to hold a TestFrame
VERIFY_ERROR (LIFECYCLE, handle_C.create<int>()); // 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<Dummy>();
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<Dummy>();
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<Dummy>() );
VERIFY_ERROR (LIFECYCLE, handle_DD.create<Dummy>() );
}
};