#ifndef _DEAPPENDLIST_HPP #define _DEAPPENDLIST_HPP /*------------------------------------------------------------------------- * drawElements C++ Base Library * ----------------------------- * * Copyright 2015 The Android Open Source Project * * 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. * *//*! * \file * \brief Fast ordered append-only container *//*--------------------------------------------------------------------*/ #include "deDefs.hpp" #include "deAtomic.h" #include "deThread.h" #include "deMemory.h" #include "deInt32.h" namespace de { /*--------------------------------------------------------------------*//*! * \brief Fast ordered append-only container * * AppendList provides data structure for recording ordered list of elements * quickly, while still providing good sequential read access speed. * It is good for example logging. * * AppendList allocates memory in blocks of blockSize elements. Choosing * too small blockSize will affect performance. * * Elements can be appended from multiple threads simultaneously but if * current block runs out, allocation of next block will happen in a single * thread and block others from inserting further elements until completed. * For that reason shared AppendList should not be used if there is a lot * of contention and instead per-thread AppendList's are recommended. *//*--------------------------------------------------------------------*/ template class AppendList { public: AppendList (size_t blockSize); ~AppendList (void); void append (const ElementType& value); size_t size (void) const { return m_numElements; } void clear (void); private: AppendList (const AppendList&); AppendList& operator= (const AppendList&); struct Block { const size_t blockNdx; ElementType* elements; Block* volatile next; Block (size_t blockNdx_, size_t size) : blockNdx (blockNdx_) , elements (reinterpret_cast(deAlignedMalloc(sizeof(ElementType)*size, deAlign32((deUint32)alignOf(), (deUint32)sizeof(void*))))) , next (DE_NULL) { } ~Block (void) { deAlignedFree(reinterpret_cast(elements)); } }; const size_t m_blockSize; volatile size_t m_numElements; Block* m_first; Block* volatile m_last; public: template class Iterator { public: Iterator (Block* curBlock_, size_t blockSize_, size_t slotNdx_) : m_curBlock (curBlock_) , m_blockSize (blockSize_) , m_slotNdx (slotNdx_) {} bool operator!= (const Iterator& other) const { return m_curBlock != other.m_curBlock || m_slotNdx != other.m_slotNdx; } bool operator== (const Iterator& other) const { return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx; } Iterator& operator++ (void) { ++m_slotNdx; if (m_slotNdx == m_blockSize) { m_slotNdx = 0; m_curBlock = m_curBlock->next; } return *this; } Iterator operator++ (int) const { Iterator copy(*this); return ++copy; } CompatibleType& operator* (void) const { return m_curBlock->elements[m_slotNdx]; } CompatibleType* operator-> (void) const { return &m_curBlock->elements[m_slotNdx]; } operator Iterator (void) const { return Iterator(m_curBlock, m_blockSize, m_slotNdx); } private: Block* m_curBlock; size_t m_blockSize; size_t m_slotNdx; }; typedef Iterator const_iterator; typedef Iterator iterator; const_iterator begin (void) const; iterator begin (void); const_iterator end (void) const; iterator end (void); }; template AppendList::AppendList (size_t blockSize) : m_blockSize (blockSize) , m_numElements (0) , m_first (new Block(0, blockSize)) , m_last (m_first) { } template AppendList::~AppendList (void) { size_t elementNdx = 0; Block* curBlock = m_first; while (curBlock) { Block* const delBlock = curBlock; curBlock = delBlock->next; // Call destructor for allocated elements for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx) delBlock->elements[elementNdx%m_blockSize].~ElementType(); delete delBlock; } DE_ASSERT(elementNdx == m_numElements); } template void AppendList::clear (void) { // \todo [2016-03-28 pyry] Make thread-safe, if possible size_t elementNdx = 0; Block* curBlock = m_first; while (curBlock) { Block* const delBlock = curBlock; curBlock = delBlock->next; // Call destructor for allocated elements for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx) delBlock->elements[elementNdx%m_blockSize].~ElementType(); if (delBlock != m_first) delete delBlock; } DE_ASSERT(elementNdx == m_numElements); m_numElements = 0; m_first->next = DE_NULL; m_last = m_first; } template void AppendList::append (const ElementType& value) { // Fetch curBlock first before allocating slot. Otherwise m_last might get updated before // this thread gets chance of reading it, leading to curBlock->blockNdx > blockNdx. Block* curBlock = m_last; deMemoryReadWriteFence(); { const size_t elementNdx = deAtomicIncrementUSize(&m_numElements) - 1; const size_t blockNdx = elementNdx / m_blockSize; const size_t slotNdx = elementNdx - (blockNdx * m_blockSize); while (curBlock->blockNdx != blockNdx) { if (curBlock->next) curBlock = curBlock->next; else { // Other thread(s) are currently allocating additional block(s) deYield(); } } // Did we allocate last slot? If so, add a new block if (slotNdx+1 == m_blockSize) { Block* const newBlock = new Block(blockNdx+1, m_blockSize); deMemoryReadWriteFence(); // At this point if any other thread is trying to allocate more blocks // they are being blocked by curBlock->next being null. This guarantees // that this thread has exclusive modify access to m_last. m_last = newBlock; deMemoryReadWriteFence(); // At this point other threads might have skipped to newBlock, but we // still have exclusive modify access to curBlock->next. curBlock->next = newBlock; deMemoryReadWriteFence(); } new (&curBlock->elements[slotNdx]) ElementType(value); } } template typename AppendList::const_iterator AppendList::begin (void) const { return const_iterator(m_first, m_blockSize, 0); } template typename AppendList::iterator AppendList::begin (void) { return iterator(m_first, m_blockSize, 0); } template typename AppendList::const_iterator AppendList::end (void) const { return const_iterator(m_last, m_blockSize, m_numElements%m_blockSize); } template typename AppendList::iterator AppendList::end (void) { return iterator(m_last, m_blockSize, m_numElements%m_blockSize); } void AppendList_selfTest (void); } // de #endif // _DEAPPENDLIST_HPP