1 #ifndef _DEAPPENDLIST_HPP
2 #define _DEAPPENDLIST_HPP
3 /*-------------------------------------------------------------------------
4 * drawElements C++ Base Library
5 * -----------------------------
7 * Copyright 2015 The Android Open Source Project
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
23 * \brief Fast ordered append-only container
24 *//*--------------------------------------------------------------------*/
35 /*--------------------------------------------------------------------*//*!
36 * \brief Fast ordered append-only container
38 * AppendList provides data structure for recording ordered list of elements
39 * quickly, while still providing good sequential read access speed.
40 * It is good for example logging.
42 * AppendList allocates memory in blocks of blockSize elements. Choosing
43 * too small blockSize will affect performance.
45 * Elements can be appended from multiple threads simultaneously but if
46 * current block runs out, allocation of next block will happen in a single
47 * thread and block others from inserting further elements until completed.
48 * For that reason shared AppendList should not be used if there is a lot
49 * of contention and instead per-thread AppendList's are recommended.
50 *//*--------------------------------------------------------------------*/
51 template<typename ElementType>
55 AppendList (size_t blockSize);
58 void append (const ElementType& value);
60 size_t size (void) const { return m_numElements; }
65 AppendList (const AppendList<ElementType>&);
66 AppendList<ElementType>& operator= (const AppendList<ElementType>&);
70 const size_t blockNdx;
71 ElementType* elements;
74 Block (size_t blockNdx_, size_t size)
75 : blockNdx (blockNdx_)
76 , elements (reinterpret_cast<ElementType*>(deAlignedMalloc(sizeof(ElementType)*size,
77 deAlign32((deUint32)alignOf<ElementType>(), (deUint32)sizeof(void*)))))
84 deAlignedFree(reinterpret_cast<void*>(elements));
88 const size_t m_blockSize;
89 volatile size_t m_numElements;
91 Block* volatile m_last;
94 template<typename CompatibleType>
98 Iterator (Block* curBlock_, size_t blockSize_, size_t slotNdx_)
99 : m_curBlock (curBlock_)
100 , m_blockSize (blockSize_)
101 , m_slotNdx (slotNdx_)
104 bool operator!= (const Iterator<CompatibleType>& other) const
106 return m_curBlock != other.m_curBlock || m_slotNdx != other.m_slotNdx;
108 bool operator== (const Iterator<CompatibleType>& other) const
110 return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx;
113 Iterator<CompatibleType>& operator++ (void)
117 if (m_slotNdx == m_blockSize)
120 m_curBlock = m_curBlock->next;
126 Iterator<CompatibleType> operator++ (int) const
128 Iterator<CompatibleType> copy(*this);
132 CompatibleType& operator* (void) const
134 return m_curBlock->elements[m_slotNdx];
137 CompatibleType* operator-> (void) const
139 return &m_curBlock->elements[m_slotNdx];
142 operator Iterator<const CompatibleType> (void) const
144 return Iterator<const CompatibleType>(m_curBlock, m_blockSize, m_slotNdx);
153 typedef Iterator<const ElementType> const_iterator;
154 typedef Iterator<ElementType> iterator;
156 const_iterator begin (void) const;
157 iterator begin (void);
159 const_iterator end (void) const;
163 template<typename ElementType>
164 AppendList<ElementType>::AppendList (size_t blockSize)
165 : m_blockSize (blockSize)
167 , m_first (new Block(0, blockSize))
172 template<typename ElementType>
173 AppendList<ElementType>::~AppendList (void)
175 size_t elementNdx = 0;
176 Block* curBlock = m_first;
180 Block* const delBlock = curBlock;
182 curBlock = delBlock->next;
184 // Call destructor for allocated elements
185 for (; elementNdx < min(m_numElements, delBlock->blockNdx*m_blockSize); ++elementNdx)
186 delBlock->elements[elementNdx%m_blockSize].~ElementType();
191 DE_ASSERT(elementNdx == m_numElements);
194 template<typename ElementType>
195 void AppendList<ElementType>::clear (void)
197 // \todo [2016-03-28 pyry] Make thread-safe, if possible
199 size_t elementNdx = 0;
200 Block* curBlock = m_first;
204 Block* const delBlock = curBlock;
206 curBlock = delBlock->next;
208 // Call destructor for allocated elements
209 for (; elementNdx < min(m_numElements, delBlock->blockNdx*m_blockSize); ++elementNdx)
210 delBlock->elements[elementNdx%m_blockSize].~ElementType();
212 if (delBlock != m_first)
216 DE_ASSERT(elementNdx == m_numElements);
222 template<typename ElementType>
223 void AppendList<ElementType>::append (const ElementType& value)
225 // Fetch curBlock first before allocating slot. Otherwise m_last might get updated before
226 // this thread gets chance of reading it, leading to curBlock->blockNdx > blockNdx.
227 Block* curBlock = m_last;
229 deMemoryReadWriteFence();
232 const size_t elementNdx = deAtomicIncrementUSize(&m_numElements) - 1;
233 const size_t blockNdx = elementNdx / m_blockSize;
234 const size_t slotNdx = elementNdx - (blockNdx * m_blockSize);
236 while (curBlock->blockNdx != blockNdx)
239 curBlock = curBlock->next;
242 // Other thread(s) are currently allocating additional block(s)
247 // Did we allocate last slot? If so, add a new block
248 if (slotNdx+1 == m_blockSize)
250 Block* const newBlock = new Block(blockNdx+1, m_blockSize);
252 deMemoryReadWriteFence();
254 // At this point if any other thread is trying to allocate more blocks
255 // they are being blocked by curBlock->next being null. This guarantees
256 // that this thread has exclusive modify access to m_last.
258 deMemoryReadWriteFence();
260 // At this point other threads might have skipped to newBlock, but we
261 // still have exclusive modify access to curBlock->next.
262 curBlock->next = newBlock;
263 deMemoryReadWriteFence();
266 new (&curBlock->elements[slotNdx]) ElementType(value);
270 template<typename ElementType>
271 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::begin (void) const
273 return const_iterator(m_first, m_blockSize, 0);
276 template<typename ElementType>
277 typename AppendList<ElementType>::iterator AppendList<ElementType>::begin (void)
279 return iterator(m_first, m_blockSize, 0);
282 template<typename ElementType>
283 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::end (void) const
285 return const_iterator(m_last, m_blockSize, m_numElements%m_blockSize);
288 template<typename ElementType>
289 typename AppendList<ElementType>::iterator AppendList<ElementType>::end (void)
291 return iterator(m_last, m_blockSize, m_numElements%m_blockSize);
294 void AppendList_selfTest (void);
298 #endif // _DEAPPENDLIST_HPP