BufferProvider interface finished thus far.
simulated lifecycle passes unit test
This commit is contained in:
parent
2fce2b1c8d
commit
7ba8ff432f
10 changed files with 178 additions and 81 deletions
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -131,10 +131,10 @@ namespace engine {
|
|||
|
||||
bool was_created_by_this_provider (BufferDescriptor const&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === Implementation === */
|
||||
|
||||
/** convenience shortcut:
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace engine {
|
|||
{ \
|
||||
emergencyCleanup(); /* EX_FREE */ \
|
||||
pBuffer_ = 0; \
|
||||
throw; \
|
||||
}
|
||||
|
||||
/** convenience shortcut: place and maintain an object within the buffer.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 === */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END
|
||||
TEST "Buffer provider diagnostics" BufferProviderProtocol_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>() );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue