123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include "jexcept.hpp"
- #include "jcrc.hpp"
- #include "thorcommon.ipp" // for CachedOutputMetaData
- #include "roxierow.hpp"
- //Classes can be used to configure the allocator, and add extra data to the end.
- //The checking needs to be done by setting a bit in the allocatorid
- class NoCheckingHelper
- {
- public:
- enum {
- extraSize = 0,
- allocatorCheckFlag = 0x00000000
- };
- static inline void setCheck(size32_t size, void * ptr) {}
- static inline bool isValid(const void * ptr) { return true; }
- };
- //NOTE: If a row requires checking then the row will also have the bit set to indicate it requires a destructor
- //so that rows are checked on destruction.
- //Therefore checking if the destructor is set for a row in isValid() to protect us from uninitialised crcs.
- class Crc16CheckingHelper
- {
- public:
- enum {
- extraSize = sizeof(unsigned short),
- allocatorCheckFlag = 0x00100000|ACTIVITY_FLAG_NEEDSDESTRUCTOR
- };
- static inline void setCheck(size32_t size, void * _ptr)
- {
- byte * ptr = static_cast<byte *>(_ptr);
- memsize_t capacity = RoxieRowCapacity(ptr);
- if (capacity < size + extraSize)
- throw MakeStringException(0, "Data was written past the end of the row - allocated %d, written %d", (size32_t)(capacity - extraSize), size);
- memset(ptr+size, 0, capacity - size - extraSize);
- unsigned short * check = reinterpret_cast<unsigned short *>(ptr + capacity - extraSize);
- *check = crc16(ptr, capacity-extraSize, 0);
- }
- static inline bool isValid(const void * _ptr)
- {
- if (RoxieRowHasDestructor(_ptr))
- {
- const byte * ptr = static_cast<const byte *>(_ptr);
- memsize_t capacity = RoxieRowCapacity(ptr);
- const unsigned short * check = reinterpret_cast<const unsigned short *>(ptr + capacity - extraSize);
- return *check == crc16(ptr, capacity-extraSize, 0);
- }
- return true;
- }
- };
- //This is here as demonstration of an alternative implementation... crc16 is possibly a bit expensive.
- class Sum16CheckingHelper
- {
- public:
- enum {
- extraSize = sizeof(unsigned short),
- allocatorCheckFlag = 0x00200000|ACTIVITY_FLAG_NEEDSDESTRUCTOR
- };
- static inline void setCheck(size32_t size, void * _ptr)
- {
- byte * ptr = static_cast<byte *>(_ptr);
- memsize_t capacity = RoxieRowCapacity(ptr);
- if (capacity < size + extraSize)
- throw MakeStringException(0, "Data was written past the end of the row - allocated %d, written %d", (size32_t)(capacity - extraSize), size);
- memset(ptr+size, 0, capacity - size - extraSize);
- unsigned short * check = reinterpret_cast<unsigned short *>(ptr + capacity - extraSize);
- *check = chksum16(ptr, capacity-extraSize);
- }
- static inline bool isValid(const void * _ptr)
- {
- if (RoxieRowHasDestructor(_ptr))
- {
- const byte * ptr = static_cast<const byte *>(_ptr);
- memsize_t capacity = RoxieRowCapacity(ptr);
- const unsigned short * check = reinterpret_cast<const unsigned short *>(ptr + capacity - extraSize);
- return chksum16(ptr, capacity-extraSize) == *check;
- }
- return true;
- }
- };
- bool isRowCheckValid(unsigned allocatorId, const void * row)
- {
- switch (allocatorId & ALLOCATORID_CHECK_MASK)
- {
- case NoCheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
- return true;
- case Crc16CheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
- return Crc16CheckingHelper::isValid(row);
- case Sum16CheckingHelper::allocatorCheckFlag & ALLOCATORID_CHECK_MASK:
- return Sum16CheckingHelper::isValid(row);
- default:
- UNIMPLEMENTED;
- }
- }
- //--------------------------------------------------------------------------------------------------------------------
- //More: Function to calculate the total size of a row - requires access to a rowallocator.
- //--------------------------------------------------------------------------------------------------------------------
- class RoxieEngineRowAllocatorBase : implements IEngineRowAllocator, public CInterface
- {
- public:
- RoxieEngineRowAllocatorBase(IRowAllocatorMetaActIdCache * _cache, roxiemem::IRowManager & _rowManager, IOutputMetaData * _meta, unsigned _activityId, unsigned _allocatorId, roxiemem::RoxieHeapFlags _createFlags)
- : createFlags(_createFlags), cache(_cache), rowManager(_rowManager), meta(_meta)
- {
- activityId = _activityId;
- allocatorId = _allocatorId;
- }
- IMPLEMENT_IINTERFACE
- //interface IEngineRowsetAllocator
- virtual const byte * * createRowset(unsigned count) override
- {
- if (count == 0)
- return NULL;
- return (const byte **) rowManager.allocate(count * sizeof(void *), allocatorId | ACTIVITY_FLAG_ISREGISTERED);
- }
- virtual void releaseRowset(unsigned count, const byte * * rowset) override
- {
- rtlReleaseRowset(count, rowset);
- }
- virtual const byte * * linkRowset(const byte * * rowset) override
- {
- return rtlLinkRowset(rowset);
- }
- virtual const byte * * appendRowOwn(const byte * * rowset, unsigned newRowCount, void * row) override
- {
- const byte * * expanded = doReallocRows(rowset, newRowCount-1, newRowCount);
- expanded[newRowCount-1] = (byte *)row;
- return expanded;
- }
- virtual const byte * * reallocRows(const byte * * rowset, unsigned oldRowCount, unsigned newRowCount) override
- {
- //New rows (if any) aren't cleared....
- return doReallocRows(rowset, oldRowCount, newRowCount);
- }
- virtual void releaseRow(const void * row) override
- {
- ReleaseRoxieRow(row);
- }
- virtual void * linkRow(const void * row) override
- {
- LinkRoxieRow(row);
- return const_cast<void *>(row);
- }
- virtual IOutputMetaData * queryOutputMeta() override
- {
- return meta.queryOriginal();
- }
- virtual unsigned queryActivityId() const override
- {
- return activityId;
- }
- virtual StringBuffer &getId(StringBuffer &idStr) override
- {
- return idStr.append(activityId); // MORE - may want more context info in here
- }
- virtual IOutputRowSerializer *createDiskSerializer(ICodeContext *ctx) override
- {
- return meta.createDiskSerializer(ctx, activityId);
- }
- virtual IOutputRowDeserializer *createDiskDeserializer(ICodeContext *ctx) override
- {
- return meta.createDiskDeserializer(ctx, activityId);
- }
- virtual IOutputRowSerializer *createInternalSerializer(ICodeContext *ctx) override
- {
- return meta.createInternalSerializer(ctx, activityId);
- }
- virtual IOutputRowDeserializer *createInternalDeserializer(ICodeContext *ctx) override
- {
- return meta.createInternalDeserializer(ctx, activityId);
- }
- virtual IEngineRowAllocator *createChildRowAllocator(const RtlTypeInfo *type) override
- {
- CriticalBlock block(cs); // Not very likely but better be safe
- if (children.empty())
- {
- for (unsigned i =0;;i++)
- {
- IOutputMetaData * childMeta = meta.queryChildMeta(i);
- if (!childMeta)
- break;
- children.append(*cache->ensure(childMeta, activityId, createFlags));
- }
- }
- ForEachItemIn(i, children)
- {
- IEngineRowAllocator & cur = children.item(i);
- if (cur.queryOutputMeta()->queryTypeInfo() == type)
- return LINK(&cur);
- }
- return NULL;
- }
- protected:
- inline const byte * * doReallocRows(const byte * * rowset, unsigned oldRowCount, unsigned newRowCount)
- {
- if (!rowset)
- return createRowset(newRowCount);
- //Occasionally (in aggregates) we may try and append to a shared rowset. In this case we need to clone the
- //target rowset. It could be that the rowset is unshared immediately, but that is inefficient at worst.
- if (RoxieRowIsShared(rowset))
- {
- const byte * * newset = createRowset(newRowCount);
- for (unsigned i=0; i < oldRowCount; i++)
- {
- const byte * cur = rowset[i];
- LinkRoxieRow(cur);
- newset[i] = cur;
- }
- ReleaseRoxieRow(rowset);
- return newset;
- }
- //This would be more efficient if previous capacity was stored by the caller - or if capacity() is more efficient
- if (newRowCount * sizeof(void *) <= RoxieRowCapacity(rowset))
- return rowset;
- memsize_t capacity;
- void * ptr = (void *)rowset;
- rowManager.resizeRow(capacity, ptr, oldRowCount * sizeof(void *), newRowCount * sizeof(void *), allocatorId | ACTIVITY_FLAG_ISREGISTERED);
- return (const byte * *)ptr;
- }
- protected:
- static CriticalSection cs; // Very unlikely to have contention, so share between all allocators
- roxiemem::RoxieHeapFlags createFlags;
- IRowAllocatorMetaActIdCache * cache;
- roxiemem::IRowManager & rowManager;
- const CachedOutputMetaData meta;
- unsigned activityId;
- unsigned allocatorId;
- IArrayOf<IEngineRowAllocator> children;
- };
- CriticalSection RoxieEngineRowAllocatorBase::cs;
- template <class CHECKER>
- class RoxieEngineFixedRowAllocator : public RoxieEngineRowAllocatorBase
- {
- public:
- RoxieEngineFixedRowAllocator(IRowAllocatorMetaActIdCache * _cache, roxiemem::IRowManager & _rowManager, IOutputMetaData * _meta, unsigned _activityId, unsigned _allocatorId, roxiemem::RoxieHeapFlags _flags)
- : RoxieEngineRowAllocatorBase(_cache, _rowManager, _meta, _activityId, _allocatorId, _flags)
- {
- unsigned flags = _flags;
- if (meta.needsDestruct() || CHECKER::allocatorCheckFlag)
- flags |= roxiemem::RHFhasdestructor;
- heap.setown(rowManager.createFixedRowHeap(meta.getFixedSize()+CHECKER::extraSize, allocatorId | ACTIVITY_FLAG_ISREGISTERED | CHECKER::allocatorCheckFlag, (roxiemem::RoxieHeapFlags)flags));
- }
- virtual void * createRow() override
- {
- return heap->allocate();
- }
- virtual void * createRow(size32_t & allocatedSize) override
- {
- allocatedSize = meta.getFixedSize();
- return heap->allocate();
- }
- virtual void * createRow(size32_t initialSize, size32_t & allocatedSize) override
- {
- size32_t fixedSize = meta.getFixedSize();
- assertex(initialSize == fixedSize);
- allocatedSize = fixedSize;
- return heap->allocate();
- }
- virtual void * resizeRow(size32_t newSize, void * row, size32_t & size) override
- {
- throwUnexpected();
- return NULL;
- }
- virtual void * finalizeRow(size32_t finalSize, void * row, size32_t oldSize) override
- {
- if (!meta.needsDestruct() && !CHECKER::allocatorCheckFlag)
- return row;
- CHECKER::setCheck(finalSize, row);
- return heap->finalizeRow(row);
- }
- virtual void gatherStats(CRuntimeStatisticCollection & stats) override
- {
- heap->gatherStats(stats);
- }
- virtual void releaseAllRows() override
- {
- heap->releaseAllRows();
- }
- protected:
- Owned<roxiemem::IFixedRowHeap> heap;
- };
- template <class CHECKER>
- class RoxieEngineVariableRowAllocator : public RoxieEngineRowAllocatorBase
- {
- public:
- RoxieEngineVariableRowAllocator(IRowAllocatorMetaActIdCache * _cache, roxiemem::IRowManager & _rowManager, IOutputMetaData * _meta, unsigned _activityId, unsigned _allocatorId, roxiemem::RoxieHeapFlags _flags)
- : RoxieEngineRowAllocatorBase(_cache, _rowManager, _meta, _activityId, _allocatorId, _flags)
- {
- unsigned flags = _flags;
- if (meta.needsDestruct() || CHECKER::allocatorCheckFlag)
- flags |= roxiemem::RHFhasdestructor;
- heap.setown(rowManager.createVariableRowHeap(allocatorId | ACTIVITY_FLAG_ISREGISTERED | CHECKER::allocatorCheckFlag, (roxiemem::RoxieHeapFlags)flags));
- }
- virtual void * createRow() override
- {
- memsize_t allocSize = meta.getInitialSize();
- memsize_t capacity;
- return heap->allocate(allocSize+CHECKER::extraSize, capacity);
- }
- virtual void * createRow(size32_t & allocatedSize) override
- {
- return doCreateRow(meta.getInitialSize(), allocatedSize);
- }
- virtual void * createRow(size32_t initialSize, size32_t & allocatedSize) override
- {
- return doCreateRow(initialSize, allocatedSize);
- }
- virtual void * resizeRow(size32_t newSize, void * row, size32_t & size) override
- {
- const size32_t oldsize = size; // don't need to include the extra checking bytes
- memsize_t newCapacity; // always initialised by resizeRow
- void * newrow = heap->resizeRow(row, oldsize, newSize+CHECKER::extraSize, newCapacity);
- if (CHECKER::extraSize)
- newCapacity -= CHECKER::extraSize;
- size = (size32_t)newCapacity;
- return newrow;
- }
- virtual void * finalizeRow(size32_t finalSize, void * row, size32_t oldSize) override
- {
- if (!meta.needsDestruct() && !CHECKER::allocatorCheckFlag)
- return row;
- void * newrow = heap->finalizeRow(row, oldSize, finalSize+CHECKER::extraSize);
- CHECKER::setCheck(finalSize, newrow);
- return newrow;
- }
- virtual void gatherStats(CRuntimeStatisticCollection & stats) override
- {
- heap->gatherStats(stats);
- }
- virtual void releaseAllRows() override
- {
- //It is not legal to call releaseAllRows on a variable size allocator - they are not allocated in a single heap
- throwUnexpected();
- }
- protected:
- void * doCreateRow(size32_t initialSize, size32_t & allocatedSize)
- {
- memsize_t newCapacity; // always initialised by allocate
- void * row = heap->allocate(initialSize + CHECKER::extraSize, newCapacity);
- //This test should get constant folded to avoid the decrement when not checked.
- if (CHECKER::extraSize)
- newCapacity -= CHECKER::extraSize;
- allocatedSize = (size32_t)newCapacity;
- return row;
- }
- protected:
- Owned<roxiemem::IVariableRowHeap> heap;
- };
- IEngineRowAllocator * createRoxieRowAllocator(IRowAllocatorMetaActIdCache * cache, roxiemem::IRowManager & rowManager, IOutputMetaData * meta, unsigned activityId, unsigned allocatorId, roxiemem::RoxieHeapFlags flags)
- {
- if (meta->getFixedSize() != 0)
- return new RoxieEngineFixedRowAllocator<NoCheckingHelper>(cache, rowManager, meta, activityId, allocatorId, flags);
- else
- return new RoxieEngineVariableRowAllocator<NoCheckingHelper>(cache, rowManager, meta, activityId, allocatorId, flags);
- }
- IEngineRowAllocator * createCrcRoxieRowAllocator(IRowAllocatorMetaActIdCache * cache, roxiemem::IRowManager & rowManager, IOutputMetaData * meta, unsigned activityId, unsigned allocatorId, roxiemem::RoxieHeapFlags flags)
- {
- if (meta->getFixedSize() != 0)
- return new RoxieEngineFixedRowAllocator<Crc16CheckingHelper>(cache, rowManager, meta, activityId, allocatorId, flags);
- else
- return new RoxieEngineVariableRowAllocator<Crc16CheckingHelper>(cache, rowManager, meta, activityId, allocatorId, flags);
- }
- #pragma pack(push,1) // hashing on members, so ensure contiguous
- struct AllocatorKey
- {
- IOutputMetaData *meta;
- unsigned activityId;
- roxiemem::RoxieHeapFlags flags;
- AllocatorKey(IOutputMetaData *_meta, unsigned _activityId, roxiemem::RoxieHeapFlags _flags)
- : meta(_meta), activityId(_activityId), flags(_flags)
- {
- }
- bool operator==(AllocatorKey const &other) const
- {
- return (meta == other.meta) && (activityId == other.activityId) && (flags == other.flags);
- }
- };
- #pragma pack(pop)
- class CAllocatorCacheItem : public OwningHTMapping<IEngineRowAllocator, AllocatorKey>
- {
- Linked<IOutputMetaData> meta;
- unsigned allocatorId;
- public:
- CAllocatorCacheItem(IEngineRowAllocator *allocator, unsigned _allocatorId, AllocatorKey &key)
- : OwningHTMapping<IEngineRowAllocator, AllocatorKey>(*allocator, key), allocatorId(_allocatorId)
- {
- meta.set(key.meta);
- }
- unsigned queryAllocatorId() const { return allocatorId; }
- };
- class CAllocatorCache : public CSimpleInterfaceOf<IRowAllocatorMetaActIdCache>
- {
- OwningSimpleHashTableOf<CAllocatorCacheItem, AllocatorKey> cache;
- IArrayOf<IEngineRowAllocator> allAllocators;
- mutable SpinLock allAllocatorsLock;
- Owned<roxiemem::IRowManager> rowManager;
- IRowAllocatorMetaActIdCacheCallback *callback;
- inline CAllocatorCacheItem *_lookup(IOutputMetaData *meta, unsigned activityId, roxiemem::RoxieHeapFlags flags) const
- {
- AllocatorKey key(meta, activityId, flags);
- return cache.find(key);
- }
- public:
- CAllocatorCache(IRowAllocatorMetaActIdCacheCallback *_callback) : callback(_callback)
- {
- }
- // IRowAllocatorMetaActIdCache
- virtual IEngineRowAllocator *ensure(IOutputMetaData * meta, unsigned activityId, roxiemem::RoxieHeapFlags flags)
- {
- SpinBlock b(allAllocatorsLock);
- for (;;)
- {
- CAllocatorCacheItem *container = _lookup(meta, activityId, flags);
- if (container)
- {
- if (0 == (roxiemem::RHFunique & flags))
- return LINK(&container->queryElement());
- // if in cache but unique, reuse allocatorId
- SpinUnblock b(allAllocatorsLock);
- return callback->createAllocator(this, meta, activityId, container->queryAllocatorId(), flags);
- }
- // NB: a RHFunique allocator, will cause 1st to be added to 'allAllocators'
- // subsequent requests for the same type of unique allocator, will share same allocatorId
- // resulting in the 1st allocator being reused by all instances for onDestroy() etc.
- assertex(allAllocators.ordinality() < ALLOCATORID_MASK);
- unsigned allocatorId = allAllocators.ordinality();
- IEngineRowAllocator *ret;
- {
- SpinUnblock b(allAllocatorsLock);
- ret = callback->createAllocator(this, meta, activityId, allocatorId, flags);
- assertex(ret);
- }
- if (allocatorId == allAllocators.ordinality())
- {
- AllocatorKey key(meta, activityId, flags);
- container = new CAllocatorCacheItem(LINK(ret), allocatorId, key);
- cache.replace(*container);
- allAllocators.append(*LINK(ret));
- return ret;
- }
- else
- {
- // someone has used the allocatorId I was going to use.. release and try again (hopefully happens very seldom)
- ret->Release();
- }
- }
- }
- virtual unsigned items() const
- {
- return allAllocators.ordinality();
- }
- // roxiemem::IRowAllocatorCache
- virtual unsigned getActivityId(unsigned cacheId) const
- {
- unsigned allocatorIndex = (cacheId & ALLOCATORID_MASK);
- SpinBlock b(allAllocatorsLock);
- if (allAllocators.isItem(allocatorIndex))
- return allAllocators.item(allocatorIndex).queryActivityId();
- else
- {
- //assert(false);
- return UNKNOWN_ACTIVITY; // Used for tracing, better than a crash...
- }
- }
- virtual StringBuffer &getActivityDescriptor(unsigned cacheId, StringBuffer &out) const
- {
- unsigned allocatorIndex = (cacheId & ALLOCATORID_MASK);
- SpinBlock b(allAllocatorsLock);
- if (allAllocators.isItem(allocatorIndex))
- return allAllocators.item(allocatorIndex).getId(out);
- else
- {
- assert(false);
- return out.append("unknown"); // Used for tracing, better than a crash...
- }
- }
- virtual void onDestroy(unsigned cacheId, void *row) const
- {
- IEngineRowAllocator *allocator;
- unsigned allocatorIndex = (cacheId & ALLOCATORID_MASK);
- {
- SpinBlock b(allAllocatorsLock); // just protect the access to the array - don't keep locked for the call of destruct or may deadlock
- if (allAllocators.isItem(allocatorIndex))
- allocator = &allAllocators.item(allocatorIndex);
- else
- {
- assert(false);
- return;
- }
- }
- if (!RoxieRowCheckValid(cacheId, row))
- {
- throw MakeStringException(0, "ERROR: crc check failure destroying row!");
- }
- allocator->queryOutputMeta()->destruct((byte *) row);
- }
- virtual void onClone(unsigned cacheId, void *row) const
- {
- IEngineRowAllocator *allocator;
- unsigned allocatorIndex = (cacheId & ALLOCATORID_MASK);
- {
- SpinBlock b(allAllocatorsLock); // just protect the access to the array - don't keep locked for the call of destruct or may deadlock
- if (allAllocators.isItem(allocatorIndex))
- allocator = &allAllocators.item(allocatorIndex);
- else
- {
- assert(false);
- return;
- }
- }
- if (!RoxieRowCheckValid(cacheId, row))
- {
- throw MakeStringException(0, "ERROR: crc check failure cloning row!");
- }
- //This should only be called if the destructor needs to be called - so don't bother checking
- ChildRowLinkerWalker walker;
- allocator->queryOutputMeta()->walkIndirectMembers((const byte *)row, walker);
- }
- virtual void checkValid(unsigned cacheId, const void *row) const
- {
- if (!RoxieRowCheckValid(cacheId, row))
- {
- throw MakeStringException(0, "ERROR: crc check failure checking row!");
- }
- }
- };
- IRowAllocatorMetaActIdCache *createRowAllocatorCache(IRowAllocatorMetaActIdCacheCallback *callback)
- {
- return new CAllocatorCache(callback);
- }
- #ifdef _USE_CPPUNIT
- #include "unittests.hpp"
- namespace roxierowtests {
- using namespace roxiemem;
- class RoxieRowAllocatorTests : public CppUnit::TestFixture
- {
- CPPUNIT_TEST_SUITE( RoxieRowAllocatorTests );
- CPPUNIT_TEST(testSetup);
- CPPUNIT_TEST(testChecking);
- CPPUNIT_TEST(testCleanup);
- CPPUNIT_TEST(testAllocatorCache);
- CPPUNIT_TEST_SUITE_END();
- const IContextLogger &logctx;
- public:
- RoxieRowAllocatorTests() : logctx(queryDummyContextLogger())
- {
- }
- ~RoxieRowAllocatorTests()
- {
- }
- protected:
- class CheckingRowAllocatorCache : public CSimpleInterface, public IRowAllocatorCache
- {
- public:
- IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
- CheckingRowAllocatorCache() { numFailures = 0; }
- virtual unsigned getActivityId(unsigned cacheId) const { return 0; }
- virtual StringBuffer &getActivityDescriptor(unsigned cacheId, StringBuffer &out) const { return out.append(cacheId); }
- virtual void onDestroy(unsigned cacheId, void *row) const
- {
- if (!RoxieRowCheckValid(cacheId, row))
- ++numFailures;
- }
- virtual void onClone(unsigned cacheId, void *row) const
- {
- }
- virtual void checkValid(unsigned cacheId, const void *row) const
- {
- if (!RoxieRowCheckValid(cacheId, row))
- ++numFailures;
- }
- mutable unsigned numFailures;
- };
- class DummyOutputMeta : public IOutputMetaData, public CInterface
- {
- public:
- DummyOutputMeta(size32_t _minSize, size32_t _fixedSize) : minSize(_minSize), fixedSize(_fixedSize) {}
- IMPLEMENT_IINTERFACE
- virtual size32_t getRecordSize(const void *rec) { return minSize; }
- virtual size32_t getFixedSize() const { return fixedSize; }
- virtual size32_t getMinRecordSize() const { return minSize; }
- virtual void toXML(const byte * self, IXmlWriter & out) {}
- virtual unsigned getVersion() const { return 0; }
- virtual unsigned getMetaFlags() { return 0; }
- virtual const RtlTypeInfo * queryTypeInfo() const { return nullptr; }
- virtual IOutputMetaData * querySerializedDiskMeta() { return this; }
- virtual void destruct(byte * self) {}
- virtual IOutputRowSerializer * createDiskSerializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
- virtual IOutputRowDeserializer * createDiskDeserializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
- virtual ISourceRowPrefetcher * createDiskPrefetcher() { return NULL; }
- virtual IOutputRowSerializer * createInternalSerializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
- virtual IOutputRowDeserializer * createInternalDeserializer(ICodeContext * ctx, unsigned activityId) { return NULL; }
- virtual void process(const byte * self, IFieldProcessor & target, unsigned from, unsigned to) {}
- virtual void walkIndirectMembers(const byte * self, IIndirectMemberVisitor & visitor) {}
- virtual IOutputMetaData * queryChildMeta(unsigned i) { return NULL; }
- virtual const RtlRecord &queryRecordAccessor(bool expand) const { UNIMPLEMENTED; }
- size32_t minSize;
- size32_t fixedSize;
- };
- void testAllocator(IOutputMetaData * meta, roxiemem::RoxieHeapFlags flags, unsigned low, unsigned high, int modify, bool checking)
- {
- CheckingRowAllocatorCache cache;
- Owned<IRowManager> rm = createRowManager(0, NULL, logctx, &cache, false);
- Owned<IEngineRowAllocator> alloc = checking ? createCrcRoxieRowAllocator(NULL, *rm, meta, 0, 0, flags) : createRoxieRowAllocator(NULL, *rm, meta, 0, 0, flags);
- for (unsigned size=low; size <= high; size++)
- {
- unsigned capacity;
- unsigned prevFailures = cache.numFailures;
- void * row = alloc->createRow(capacity);
- if (low != high)
- row = alloc->resizeRow(size, row, capacity);
- for (unsigned i1=0; i1 < capacity; i1++)
- ((byte *)row)[i1] = i1;
- const void * final = alloc->finalizeRow(capacity, row, capacity);
- for (unsigned i2=0; i2 < capacity; i2++)
- {
- ASSERT(((byte *)row)[i2] == i2);
- }
- if (modify != 0)
- {
- if (modify < 0)
- ((byte *)row)[0]++;
- else
- ((byte *)row)[size-1]++;
- }
- ReleaseRoxieRow(row);
- if (modify == 0)
- {
- ASSERT(prevFailures == cache.numFailures);
- }
- else
- {
- ASSERT(prevFailures+1 == cache.numFailures);
- }
- }
- }
- void testAllocator(IOutputMetaData * meta, roxiemem::RoxieHeapFlags flags, unsigned low, unsigned high)
- {
- testAllocator(meta, flags, low, high, 0, false);
- testAllocator(meta, flags, low, high, 0, true);
- testAllocator(meta, flags, low, high, -1, true);
- testAllocator(meta, flags, low, high, +1, true);
- }
- void testSetup()
- {
- setTotalMemoryLimit(false, true, false, 40*HEAP_ALIGNMENT_SIZE, 0, NULL, NULL);
- }
- void testCleanup()
- {
- releaseRoxieHeap();
- }
- void testChecking()
- {
- Owned<IRowManager> rm = createRowManager(0, NULL, logctx, NULL, false);
- for (unsigned fixedSize=1; fixedSize<64; fixedSize++)
- {
- DummyOutputMeta meta(fixedSize, fixedSize);
- testAllocator(&meta, RHFnone, fixedSize, fixedSize);
- testAllocator(&meta, RHFpacked, fixedSize, fixedSize);
- }
- for (unsigned varSize=1; varSize<64; varSize++)
- {
- DummyOutputMeta meta(varSize, 0);
- testAllocator(&meta, RHFnone, varSize, varSize);
- testAllocator(&meta, RHFnone, 1, varSize);
- }
- }
- void testAllocatorCache()
- {
- IArrayOf<IOutputMetaData> metas;
- Owned<IRowManager> rm = createRowManager(0, NULL, logctx, NULL, false);
- class CAllocatorCallback : implements IRowAllocatorMetaActIdCacheCallback
- {
- IRowManager *rm;
- public:
- CAllocatorCallback(IRowManager *_rm) : rm(_rm)
- {
- }
- virtual IEngineRowAllocator *createAllocator(IRowAllocatorMetaActIdCache * cache, IOutputMetaData *meta, unsigned activityId, unsigned cacheId, roxiemem::RoxieHeapFlags flags) const
- {
- return createRoxieRowAllocator(cache, *rm, meta, activityId, cacheId, flags);
- }
- } callback(rm);
- Owned<IRowAllocatorMetaActIdCache> allocatorCache = createRowAllocatorCache(&callback);
- // create 64 allocators, 32 different activityId's
- for (unsigned fixedSize=1; fixedSize<=64; fixedSize++)
- {
- DummyOutputMeta *meta = new DummyOutputMeta(fixedSize, fixedSize);
- metas.append(*meta);
- unsigned activityId = 1 + ((fixedSize-1) % 32); // i.e. make an id, so half are duplicates
- Owned<IEngineRowAllocator> allocator = allocatorCache->ensure(meta, activityId, roxiemem::RHFnone);
- }
- // test that 64 in cache
- ASSERT(allocatorCache->items() == 64);
- // test ensure again
- for (unsigned fixedSize=1; fixedSize<=64; fixedSize++)
- {
- unsigned activityId = 1 + ((fixedSize-1) % 32); // i.e. make an id, so half are duplicates
- IOutputMetaData *meta = &metas.item(fixedSize-1); // from 1st round
- Owned<IEngineRowAllocator> allocator = allocatorCache->ensure(meta, activityId, roxiemem::RHFnone);
- }
- ASSERT(allocatorCache->items() == 64);
- metas.kill();
- allocatorCache.clear();
- }
- };
- CPPUNIT_TEST_SUITE_REGISTRATION( RoxieRowAllocatorTests );
- CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( RoxieRowAllocatorTests, "RoxieRowAllocatorTests" );
- } // namespace roxiemem
- #endif
|