/* TRACKING-HEAP-BLOCK-PROVIDER.hpp - plain heap allocating BufferProvider implementation for tests Copyright (C) 2011, Hermann Vosseler   **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" #include "lib/hash-value.h" #include "steam/engine/buffer-provider.hpp" #include "lib/scoped-ptrvect.hpp" #include #include 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 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 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 pool_; ScopedPtrVect 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 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 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 (memoryBlock.accessMemory())); REQUIRE (converted); return *converted; } }} // namespace steam::engine #endif