Merge "Report tests using Draw*BaseVertex as NotSupported" am: f96636fdfa
[platform/upstream/VK-GL-CTS.git] / framework / delibs / decpp / deAppendList.hpp
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
60         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
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*)))))
78                         , next          (DE_NULL)
79                 {
80                 }
81
82                 ~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:
98                                                                         Iterator                                                (Block* curBlock_, size_t blockSize_, size_t slotNdx_)
99                                                                                                                                                 : m_curBlock    (curBlock_)
100                                                                                                                                                 , m_blockSize   (blockSize_)
101                                                                                                                                                 , m_slotNdx             (slotNdx_)
102                 {}
103
104                 bool                                            operator!=                                              (const Iterator<CompatibleType>& other) const
105                 {
106                         return m_curBlock != other.m_curBlock || m_slotNdx != other.m_slotNdx;
107                 }
108                 bool                                            operator==                                              (const Iterator<CompatibleType>& other) const
109                 {
110                         return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx;
111                 }
112
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
126                 Iterator<CompatibleType>        operator++                                              (int) const
127                 {
128                         Iterator<CompatibleType> copy(*this);
129                         return ++copy;
130                 }
131
132                 CompatibleType&                         operator*                                               (void) const
133                 {
134                         return m_curBlock->elements[m_slotNdx];
135                 }
136
137                 CompatibleType*                         operator->                                              (void) const
138                 {
139                         return &m_curBlock->elements[m_slotNdx];
140                 }
141
142                 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>
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>
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>
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>
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>
272 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>
278 typename AppendList<ElementType>::iterator AppendList<ElementType>::begin (void)
279 {
280         return iterator(m_first, m_blockSize, 0);
281 }
282
283 template<typename ElementType>
284 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>
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