LUMIERA.clone/src/steam/engine/tracking-heap-block-provider.hpp

182 lines
5.5 KiB
C++
Raw Normal View History

/*
TRACKING-HEAP-BLOCK-PROVIDER.hpp - plain heap allocating BufferProvider implementation for tests
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2011, Hermann Vosseler <Ichthyostega@web.de>
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **Lumiera** 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. See the file COPYING for further details.
*/
/** @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"
2012-12-02 23:03:37 +01:00
#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;
2012-12-02 23:03:37 +01:00
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_;
2011-11-21 02:28:44 +01:00
bool was_released_;
public:
explicit
Block(size_t bufferSize)
2011-11-21 02:28:44 +01:00
: storage_(bufferSize? new char[bufferSize] : NULL)
, was_released_(false)
{ }
bool
was_used() const
{
2011-11-21 02:28:44 +01:00
return bool(storage_);
}
bool
was_closed() const
{
2011-11-21 02:28:44 +01:00
return was_released_;
}
void*
accessMemory() const
{
2011-11-21 02:28:44 +01:00
REQUIRE (storage_, "Block was never prepared for use");
return storage_.get();
}
void
markReleased()
{
2011-11-21 02:28:44 +01:00
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);
void markAllEmitted();
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()));
2011-11-21 02:28:44 +01:00
REQUIRE (converted);
return *converted;
}
}} // namespace steam::engine
#endif