Code clean-up: mark all buffers with a dedicated tagging type The point in question is: if we work the LocalTag into the type-hash, could it be possible to miss an existing entry in the metadata registry? This could cause two entries to be locked for a single buffer address, leading to data corruption. As far as I can see, in the current usage this would not happen, but unfortunately this problem can not be ruled out, since the BufferProvider API and protocol is designed to be open for various usage patterns. However, the same potentially disastrous pattern could also materialise when registering two different buffer types, and then locking each for the same buffer location.
188 lines
5.8 KiB
C++
188 lines
5.8 KiB
C++
/*
|
|
TRACKING-HEAP-BLOCK-PROVIDER.hpp - plain heap allocating BufferProvider implementation for tests
|
|
|
|
Copyright (C) Lumiera.org
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
/** @file tracking-heap-block-provider.hpp
|
|
** Dummy implementation of the BufferProvider interface to support writing unit tests.
|
|
** This BufferProvider is especially straight forward and brain dead: it just claims
|
|
** more and more heap blocks and never releases any memory dynamically. This allows
|
|
** to investigate additional tracking status flags for each allocated block after
|
|
** the fact.
|
|
**
|
|
** The allocated buffers are numbered with a simple ascending sequence of integers,
|
|
** used as LocalTag (see BufferMetadata). Clients can just request a Buffer with the
|
|
** given number, causing that block to be allocated. There is a "backdoor", allowing
|
|
** to access any allocated block, even if it is considered "released" by the terms
|
|
** of the usual lifecycle. Only when the provider object itself gets destroyed,
|
|
** all allocated blocks will be discarded.
|
|
**
|
|
** @see DiagnosticOutputSlot
|
|
** @see DiagnosticBufferProvider
|
|
** @see buffer-provider-protocol-test.cpp
|
|
*/
|
|
|
|
#ifndef STEAM_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H
|
|
#define STEAM_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
#include "lib/hash-value.h"
|
|
#include "steam/engine/buffer-provider.hpp"
|
|
#include "lib/scoped-ptrvect.hpp"
|
|
|
|
#include <unordered_map>
|
|
#include <memory>
|
|
|
|
|
|
namespace steam {
|
|
namespace engine {
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
using lib::ScopedPtrVect;
|
|
using lib::HashVal;
|
|
|
|
namespace diagn {
|
|
|
|
using std::unique_ptr;
|
|
|
|
|
|
/**
|
|
* Helper for implementing a diagnostic BufferProvider:
|
|
* A block of heap allocated storage, with the capability
|
|
* to store some additional tracking information.
|
|
*/
|
|
class Block
|
|
: util::NonCopyable
|
|
{
|
|
unique_ptr<char[]> storage_;
|
|
|
|
bool was_released_;
|
|
|
|
public:
|
|
explicit
|
|
Block(size_t bufferSize)
|
|
: storage_(bufferSize? new char[bufferSize] : NULL)
|
|
, was_released_(false)
|
|
{ }
|
|
|
|
bool
|
|
was_used() const
|
|
{
|
|
return bool(storage_);
|
|
}
|
|
|
|
bool
|
|
was_closed() const
|
|
{
|
|
return was_released_;
|
|
}
|
|
|
|
void*
|
|
accessMemory() const
|
|
{
|
|
REQUIRE (storage_, "Block was never prepared for use");
|
|
return storage_.get();
|
|
}
|
|
|
|
void
|
|
markReleased()
|
|
{
|
|
was_released_ = true;
|
|
}
|
|
};
|
|
|
|
class BlockPool;
|
|
|
|
typedef std::unordered_map<HashVal,BlockPool> PoolTable;
|
|
}
|
|
|
|
|
|
/**
|
|
* simple BufferProvider implementation with additional allocation tracking.
|
|
* @internal used as PImpl by DiagnosticBufferProvider and DiagnosticOutputSlot.
|
|
*
|
|
* This dummy implementation of the BufferProvider interface uses a linearly growing
|
|
* table of heap allocated buffer blocks, which will never be discarded, unless the object
|
|
* is discarded as a whole. There is an additional testing/diagnostics API to access the
|
|
* tracked usage information, even when blocks are already marked as "released".
|
|
*/
|
|
class TrackingHeapBlockProvider
|
|
: public BufferProvider
|
|
{
|
|
unique_ptr<diagn::PoolTable> pool_;
|
|
ScopedPtrVect<diagn::Block> outSeq_;
|
|
|
|
public:
|
|
/* === BufferProvider interface === */
|
|
|
|
virtual uint prepareBuffers (uint count, HashVal typeID) override;
|
|
virtual BuffHandle provideLockedBuffer (HashVal typeID) override;
|
|
virtual void mark_emitted (HashVal, LocalTag const&) override;
|
|
virtual void detachBuffer (HashVal, LocalTag const&, Buff&) override;
|
|
|
|
public:
|
|
TrackingHeapBlockProvider();
|
|
virtual ~TrackingHeapBlockProvider();
|
|
|
|
size_t emittedCnt() const;
|
|
|
|
diagn::Block& access_emitted (uint bufferID);
|
|
|
|
template<typename TY>
|
|
TY& accessAs (uint bufferID);
|
|
|
|
private:
|
|
bool withinOutputSequence (uint bufferID) const;
|
|
diagn::BlockPool& getBlockPoolFor (HashVal typeID);
|
|
diagn::Block* locateBlock (HashVal typeID, void*);
|
|
diagn::Block* searchInOutSeqeuence (void* storage);
|
|
};
|
|
|
|
|
|
|
|
/** convenience shortcut: access the buffer with the given number,
|
|
* then try to convert the raw memory to the templated type.
|
|
* @throw error::Invalid if the required fame number is beyond
|
|
* the number of buffers marked as "emitted"
|
|
* @throw error::Fatal if conversion is not possible or the
|
|
* conversion path chosen doesn't work (which might
|
|
* be due to RTTI indicating an incompatible type).
|
|
*/
|
|
template<typename TY>
|
|
TY&
|
|
TrackingHeapBlockProvider::accessAs (uint bufferID)
|
|
{
|
|
if (!withinOutputSequence (bufferID))
|
|
throw error::Invalid ("Buffer with the given ID not yet emitted");
|
|
|
|
diagn::Block& memoryBlock = access_emitted (bufferID);
|
|
TY* converted = std::launder (reinterpret_cast<TY*> (memoryBlock.accessMemory()));
|
|
|
|
REQUIRE (converted);
|
|
return *converted;
|
|
}
|
|
|
|
|
|
|
|
|
|
}} // namespace steam::engine
|
|
#endif
|