xref: /aosp_15_r20/external/deqp/framework/delibs/decpp/deAppendList.hpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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