1 #ifndef _DEAPPENDLIST_HPP
2 #define _DEAPPENDLIST_HPP
3 /*-------------------------------------------------------------------------
4 * drawElements C++ Base Library
5 * -----------------------------
6 *
7 * Copyright 2015 The Android Open Source Project
8 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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.
20 *
21 *//*!
22 * \file
23 * \brief Fast ordered append-only container
24 *//*--------------------------------------------------------------------*/
25
26 #include "deDefs.hpp"
27 #include "deAtomic.h"
28 #include "deThread.h"
29 #include "deMemory.h"
30 #include "deInt32.h"
31
32 namespace de
33 {
34
35 /*--------------------------------------------------------------------*//*!
36 * \brief Fast ordered append-only container
37 *
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.
41 *
42 * AppendList allocates memory in blocks of blockSize elements. Choosing
43 * too small blockSize will affect performance.
44 *
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>
52 class AppendList
53 {
54 public:
55 AppendList (size_t blockSize);
56 ~AppendList (void);
57
58 void append (const ElementType& value);
59
size(void) const60 size_t size (void) const { return m_numElements; }
61
62 void clear (void);
63
64 private:
65 AppendList (const AppendList<ElementType>&);
66 AppendList<ElementType>& operator= (const AppendList<ElementType>&);
67
68 struct Block
69 {
70 const size_t blockNdx;
71 ElementType* elements;
72 Block* volatile next;
73
Blockde::AppendList::Block74 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*)))))
78 , next (DE_NULL)
79 {
80 }
81
~Blockde::AppendList::Block82 ~Block (void)
83 {
84 deAlignedFree(reinterpret_cast<void*>(elements));
85 }
86 };
87
88 const size_t m_blockSize;
89 volatile size_t m_numElements;
90 Block* m_first;
91 Block* volatile m_last;
92
93 public:
94 template<typename CompatibleType>
95 class Iterator
96 {
97 public:
Iterator(Block * curBlock_,size_t blockSize_,size_t slotNdx_)98 Iterator (Block* curBlock_, size_t blockSize_, size_t slotNdx_)
99 : m_curBlock (curBlock_)
100 , m_blockSize (blockSize_)
101 , m_slotNdx (slotNdx_)
102 {}
103
operator !=(const Iterator<CompatibleType> & other) const104 bool operator!= (const Iterator<CompatibleType>& other) const
105 {
106 return m_curBlock != other.m_curBlock || m_slotNdx != other.m_slotNdx;
107 }
operator ==(const Iterator<CompatibleType> & other) const108 bool operator== (const Iterator<CompatibleType>& other) const
109 {
110 return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx;
111 }
112
operator ++(void)113 Iterator<CompatibleType>& operator++ (void)
114 {
115 ++m_slotNdx;
116
117 if (m_slotNdx == m_blockSize)
118 {
119 m_slotNdx = 0;
120 m_curBlock = m_curBlock->next;
121 }
122
123 return *this;
124 }
125
operator ++(int) const126 Iterator<CompatibleType> operator++ (int) const
127 {
128 Iterator<CompatibleType> copy(*this);
129 return ++copy;
130 }
131
operator *(void) const132 CompatibleType& operator* (void) const
133 {
134 return m_curBlock->elements[m_slotNdx];
135 }
136
operator ->(void) const137 CompatibleType* operator-> (void) const
138 {
139 return &m_curBlock->elements[m_slotNdx];
140 }
141
operator Iterator<const CompatibleType>(void) const142 operator Iterator<const CompatibleType> (void) const
143 {
144 return Iterator<const CompatibleType>(m_curBlock, m_blockSize, m_slotNdx);
145 }
146
147 private:
148 Block* m_curBlock;
149 size_t m_blockSize;
150 size_t m_slotNdx;
151 };
152
153 typedef Iterator<const ElementType> const_iterator;
154 typedef Iterator<ElementType> iterator;
155
156 const_iterator begin (void) const;
157 iterator begin (void);
158
159 const_iterator end (void) const;
160 iterator end (void);
161 };
162
163 template<typename ElementType>
AppendList(size_t blockSize)164 AppendList<ElementType>::AppendList (size_t blockSize)
165 : m_blockSize (blockSize)
166 , m_numElements (0)
167 , m_first (new Block(0, blockSize))
168 , m_last (m_first)
169 {
170 }
171
172 template<typename ElementType>
~AppendList(void)173 AppendList<ElementType>::~AppendList (void)
174 {
175 size_t elementNdx = 0;
176 Block* curBlock = m_first;
177
178 while (curBlock)
179 {
180 Block* const delBlock = curBlock;
181
182 curBlock = delBlock->next;
183
184 // Call destructor for allocated elements
185 for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx)
186 delBlock->elements[elementNdx%m_blockSize].~ElementType();
187
188 delete delBlock;
189 }
190
191 DE_ASSERT(elementNdx == m_numElements);
192 }
193
194 template<typename ElementType>
clear(void)195 void AppendList<ElementType>::clear (void)
196 {
197 // \todo [2016-03-28 pyry] Make thread-safe, if possible
198
199 size_t elementNdx = 0;
200 Block* curBlock = m_first;
201
202 while (curBlock)
203 {
204 Block* const delBlock = curBlock;
205
206 curBlock = delBlock->next;
207
208 // Call destructor for allocated elements
209 for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx)
210 delBlock->elements[elementNdx%m_blockSize].~ElementType();
211
212 if (delBlock != m_first)
213 delete delBlock;
214 }
215
216 DE_ASSERT(elementNdx == m_numElements);
217
218 m_numElements = 0;
219 m_first->next = DE_NULL;
220 m_last = m_first;
221 }
222
223 template<typename ElementType>
append(const ElementType & value)224 void AppendList<ElementType>::append (const ElementType& value)
225 {
226 // Fetch curBlock first before allocating slot. Otherwise m_last might get updated before
227 // this thread gets chance of reading it, leading to curBlock->blockNdx > blockNdx.
228 Block* curBlock = m_last;
229
230 deMemoryReadWriteFence();
231
232 {
233 const size_t elementNdx = deAtomicIncrementUSize(&m_numElements) - 1;
234 const size_t blockNdx = elementNdx / m_blockSize;
235 const size_t slotNdx = elementNdx - (blockNdx * m_blockSize);
236
237 while (curBlock->blockNdx != blockNdx)
238 {
239 if (curBlock->next)
240 curBlock = curBlock->next;
241 else
242 {
243 // Other thread(s) are currently allocating additional block(s)
244 deYield();
245 }
246 }
247
248 // Did we allocate last slot? If so, add a new block
249 if (slotNdx+1 == m_blockSize)
250 {
251 Block* const newBlock = new Block(blockNdx+1, m_blockSize);
252
253 deMemoryReadWriteFence();
254
255 // At this point if any other thread is trying to allocate more blocks
256 // they are being blocked by curBlock->next being null. This guarantees
257 // that this thread has exclusive modify access to m_last.
258 m_last = newBlock;
259 deMemoryReadWriteFence();
260
261 // At this point other threads might have skipped to newBlock, but we
262 // still have exclusive modify access to curBlock->next.
263 curBlock->next = newBlock;
264 deMemoryReadWriteFence();
265 }
266
267 new (&curBlock->elements[slotNdx]) ElementType(value);
268 }
269 }
270
271 template<typename ElementType>
begin(void) const272 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::begin (void) const
273 {
274 return const_iterator(m_first, m_blockSize, 0);
275 }
276
277 template<typename ElementType>
begin(void)278 typename AppendList<ElementType>::iterator AppendList<ElementType>::begin (void)
279 {
280 return iterator(m_first, m_blockSize, 0);
281 }
282
283 template<typename ElementType>
end(void) const284 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::end (void) const
285 {
286 return const_iterator(m_last, m_blockSize, m_numElements%m_blockSize);
287 }
288
289 template<typename ElementType>
end(void)290 typename AppendList<ElementType>::iterator AppendList<ElementType>::end (void)
291 {
292 return iterator(m_last, m_blockSize, m_numElements%m_blockSize);
293 }
294
295 void AppendList_selfTest (void);
296
297 } // de
298
299 #endif // _DEAPPENDLIST_HPP
300