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
61 {
62 return m_numElements;
63 }
64
65 void clear(void);
66
67 private:
68 AppendList(const AppendList<ElementType> &);
69 AppendList<ElementType> &operator=(const AppendList<ElementType> &);
70
71 struct Block
72 {
73 const size_t blockNdx;
74 ElementType *elements;
75 Block *volatile next;
76
Blockde::AppendList::Block77 Block(size_t blockNdx_, size_t size)
78 : blockNdx(blockNdx_)
79 , elements(reinterpret_cast<ElementType *>(deAlignedMalloc(
80 sizeof(ElementType) * size, deAlign32((uint32_t)alignOf<ElementType>(), (uint32_t)sizeof(void *)))))
81 , next(DE_NULL)
82 {
83 }
84
~Blockde::AppendList::Block85 ~Block(void)
86 {
87 deAlignedFree(reinterpret_cast<void *>(elements));
88 }
89 };
90
91 const size_t m_blockSize;
92 volatile size_t m_numElements;
93 Block *m_first;
94 Block *volatile m_last;
95
96 public:
97 template <typename CompatibleType>
98 class Iterator
99 {
100 public:
Iterator(Block * curBlock_,size_t blockSize_,size_t slotNdx_)101 Iterator(Block *curBlock_, size_t blockSize_, size_t slotNdx_)
102 : m_curBlock(curBlock_)
103 , m_blockSize(blockSize_)
104 , m_slotNdx(slotNdx_)
105 {
106 }
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 }
operator ==(const Iterator<CompatibleType> & other) const112 bool operator==(const Iterator<CompatibleType> &other) const
113 {
114 return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx;
115 }
116
operator ++(void)117 Iterator<CompatibleType> &operator++(void)
118 {
119 ++m_slotNdx;
120
121 if (m_slotNdx == m_blockSize)
122 {
123 m_slotNdx = 0;
124 m_curBlock = m_curBlock->next;
125 }
126
127 return *this;
128 }
129
operator ++(int) const130 Iterator<CompatibleType> operator++(int) const
131 {
132 Iterator<CompatibleType> copy(*this);
133 return ++copy;
134 }
135
operator *(void) const136 CompatibleType &operator*(void) const
137 {
138 return m_curBlock->elements[m_slotNdx];
139 }
140
operator ->(void) const141 CompatibleType *operator->(void) const
142 {
143 return &m_curBlock->elements[m_slotNdx];
144 }
145
operator Iterator<const CompatibleType>(void) const146 operator Iterator<const CompatibleType>(void) const
147 {
148 return Iterator<const CompatibleType>(m_curBlock, m_blockSize, m_slotNdx);
149 }
150
151 private:
152 Block *m_curBlock;
153 size_t m_blockSize;
154 size_t m_slotNdx;
155 };
156
157 typedef Iterator<const ElementType> const_iterator;
158 typedef Iterator<ElementType> iterator;
159
160 const_iterator begin(void) const;
161 iterator begin(void);
162
163 const_iterator end(void) const;
164 iterator end(void);
165 };
166
167 template <typename ElementType>
AppendList(size_t blockSize)168 AppendList<ElementType>::AppendList(size_t blockSize)
169 : m_blockSize(blockSize)
170 , m_numElements(0)
171 , m_first(new Block(0, blockSize))
172 , m_last(m_first)
173 {
174 }
175
176 template <typename ElementType>
~AppendList(void)177 AppendList<ElementType>::~AppendList(void)
178 {
179 size_t elementNdx = 0;
180 Block *curBlock = m_first;
181
182 while (curBlock)
183 {
184 Block *const delBlock = curBlock;
185
186 curBlock = delBlock->next;
187
188 // Call destructor for allocated elements
189 for (; elementNdx < min(m_numElements, (delBlock->blockNdx + 1) * m_blockSize); ++elementNdx)
190 delBlock->elements[elementNdx % m_blockSize].~ElementType();
191
192 delete delBlock;
193 }
194
195 DE_ASSERT(elementNdx == m_numElements);
196 }
197
198 template <typename ElementType>
clear(void)199 void AppendList<ElementType>::clear(void)
200 {
201 // \todo [2016-03-28 pyry] Make thread-safe, if possible
202
203 size_t elementNdx = 0;
204 Block *curBlock = m_first;
205
206 while (curBlock)
207 {
208 Block *const delBlock = curBlock;
209
210 curBlock = delBlock->next;
211
212 // Call destructor for allocated elements
213 for (; elementNdx < min(m_numElements, (delBlock->blockNdx + 1) * m_blockSize); ++elementNdx)
214 delBlock->elements[elementNdx % m_blockSize].~ElementType();
215
216 if (delBlock != m_first)
217 delete delBlock;
218 }
219
220 DE_ASSERT(elementNdx == m_numElements);
221
222 m_numElements = 0;
223 m_first->next = DE_NULL;
224 m_last = m_first;
225 }
226
227 template <typename ElementType>
append(const ElementType & value)228 void AppendList<ElementType>::append(const ElementType &value)
229 {
230 // Fetch curBlock first before allocating slot. Otherwise m_last might get updated before
231 // this thread gets chance of reading it, leading to curBlock->blockNdx > blockNdx.
232 Block *curBlock = m_last;
233
234 deMemoryReadWriteFence();
235
236 {
237 const size_t elementNdx = deAtomicIncrementUSize(&m_numElements) - 1;
238 const size_t blockNdx = elementNdx / m_blockSize;
239 const size_t slotNdx = elementNdx - (blockNdx * m_blockSize);
240
241 while (curBlock->blockNdx != blockNdx)
242 {
243 if (curBlock->next)
244 curBlock = curBlock->next;
245 else
246 {
247 // Other thread(s) are currently allocating additional block(s)
248 deYield();
249 }
250 }
251
252 // Did we allocate last slot? If so, add a new block
253 if (slotNdx + 1 == m_blockSize)
254 {
255 Block *const newBlock = new Block(blockNdx + 1, m_blockSize);
256
257 deMemoryReadWriteFence();
258
259 // At this point if any other thread is trying to allocate more blocks
260 // they are being blocked by curBlock->next being null. This guarantees
261 // that this thread has exclusive modify access to m_last.
262 m_last = newBlock;
263 deMemoryReadWriteFence();
264
265 // At this point other threads might have skipped to newBlock, but we
266 // still have exclusive modify access to curBlock->next.
267 curBlock->next = newBlock;
268 deMemoryReadWriteFence();
269 }
270
271 new (&curBlock->elements[slotNdx]) ElementType(value);
272 }
273 }
274
275 template <typename ElementType>
begin(void) const276 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::begin(void) const
277 {
278 return const_iterator(m_first, m_blockSize, 0);
279 }
280
281 template <typename ElementType>
begin(void)282 typename AppendList<ElementType>::iterator AppendList<ElementType>::begin(void)
283 {
284 return iterator(m_first, m_blockSize, 0);
285 }
286
287 template <typename ElementType>
end(void) const288 typename AppendList<ElementType>::const_iterator AppendList<ElementType>::end(void) const
289 {
290 return const_iterator(m_last, m_blockSize, m_numElements % m_blockSize);
291 }
292
293 template <typename ElementType>
end(void)294 typename AppendList<ElementType>::iterator AppendList<ElementType>::end(void)
295 {
296 return iterator(m_last, m_blockSize, m_numElements % m_blockSize);
297 }
298
299 void AppendList_selfTest(void);
300
301 } // namespace de
302
303 #endif // _DEAPPENDLIST_HPP
304