1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Solutions component.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 
42 #ifndef QSCRIPTGC_P_H
43 #define QSCRIPTGC_P_H
44 
45 //
46 //  W A R N I N G
47 //  -------------
48 //
49 // This file is not part of the Qt API.  It exists purely as an
50 // implementation detail.  This header file may change from version to
51 // version without notice, or even be removed.
52 //
53 // We mean it.
54 //
55 
56 #if defined(Q_OS_VXWORKS) && defined(m_free)
57 #  undef m_free
58 #endif
59 
60 #include <qglobal.h>
61 
62 
63 #include <QtDebug>
64 #include <new>
65 
66 #include "qscriptmemorypool_p.h"
67 
68 QT_BEGIN_NAMESPACE
69 
70 namespace QScript {
71 
72 class GCBlock
73 {
74 public:
75     GCBlock *next;
76 
77     union {
78         int generation;
79         uint flags;
80     };
81 
82 public:
GCBlock(GCBlock * n)83     inline GCBlock(GCBlock *n):
84         next(n), flags(0) {}
85 
data()86     inline void *data()
87     { return reinterpret_cast<char *>(this) + sizeof(GCBlock); }
88 
get(void * ptr)89     inline static GCBlock *get(void *ptr)
90     {
91         char *where = reinterpret_cast<char *>(ptr);
92         return reinterpret_cast<GCBlock *>(where - sizeof(GCBlock));
93     }
94 };
95 
96 template <typename _Tp>
97 class GCAlloc
98 {
99 private:
100     int m_new_allocated_blocks;
101     int m_free_blocks;
102     int m_new_allocated_extra_bytes;
103     GCBlock *m_head;
104     GCBlock *m_current;
105     GCBlock *m_free;
106     bool m_blocked_gc;
107     bool m_force_gc;
108     bool m_sweeping;
109     MemoryPool pool;
110     _Tp trivial;
111 
112 public:
113     enum { MaxNumberOfBlocks = 1 << 14 };
114     enum { MaxNumberOfExtraBytes = 0x800000 };
115 
116 public:
GCAlloc()117     inline GCAlloc():
118         m_new_allocated_blocks(0),
119         m_free_blocks(0),
120         m_new_allocated_extra_bytes(0),
121         m_head(0),
122         m_current(0),
123         m_free(0),
124         m_blocked_gc(false),
125         m_force_gc(false),
126         m_sweeping(false) {
127         trivial.reset();
128     }
129 
~GCAlloc()130     inline ~GCAlloc() {
131     }
132 
destruct()133     inline void destruct() {
134         m_sweeping = true;
135         GCBlock *blk = m_free;
136 
137         if (! blk) {
138             blk = m_head;
139             m_head = 0;
140         }
141 
142         while (blk) {
143             GCBlock *was = blk;
144             blk = blk->next;
145 
146             Q_ASSERT(was->data());
147             _Tp *data = reinterpret_cast<_Tp*>(was->data());
148             data->~_Tp();
149             blk->~GCBlock();
150 
151             if (! blk && m_head) {
152                 blk = m_head;
153                 m_head = 0;
154             }
155         }
156         m_sweeping = false;
157     }
158 
newAllocatedBlocks()159     inline int newAllocatedBlocks() const { return m_new_allocated_blocks; }
freeBlocks()160     inline int freeBlocks() const { return m_free_blocks; }
161 
operator()162     inline _Tp *operator()(int generation)
163     {
164         GCBlock *previous = m_current;
165         void *where = 0;
166 
167         if (! m_free) {
168             Q_ASSERT (m_free_blocks == 0);
169             where = pool.allocate(sizeof(GCBlock) + sizeof(_Tp));
170             ++m_new_allocated_blocks;
171             (void) new (reinterpret_cast<char*>(where) + sizeof(GCBlock)) _Tp();
172         } else {
173             --m_free_blocks;
174             where = m_free;
175             m_free = m_free->next;
176 
177             if (! m_free)
178                 m_force_gc = true;
179         }
180 
181         m_current = new (where) GCBlock(0);
182 
183         if (! previous) {
184             Q_ASSERT(! m_head);
185             m_head = m_current;
186         } else {
187             previous->next = m_current;
188         }
189         m_current->generation = generation;
190 
191         return reinterpret_cast<_Tp*> (m_current->data());
192     }
193 
blocked()194     inline bool blocked() const
195     {
196         return m_blocked_gc;
197     }
198 
sweeping()199     inline bool sweeping() const
200     {
201         return m_sweeping;
202     }
203 
blockGC(bool block)204     inline bool blockGC(bool block)
205     {
206         bool was = m_blocked_gc;
207         m_blocked_gc = block;
208         return was;
209     }
210 
requestGC()211     inline void requestGC()
212     {
213         m_force_gc = true;
214     }
215 
adjustBytesAllocated(int bytes)216     inline void adjustBytesAllocated(int bytes)
217     { m_new_allocated_extra_bytes += bytes; }
218 
poll()219     inline bool poll()
220     {
221         if (m_blocked_gc || ! m_head)
222             return false;
223 
224         else if (m_force_gc) {
225             m_force_gc = false;
226             return true;
227         }
228 
229         else if (m_free && ! m_free->next)
230             return true;
231 
232         return (m_new_allocated_blocks >= MaxNumberOfBlocks)
233             || ((m_new_allocated_extra_bytes >= MaxNumberOfExtraBytes)
234                 && (m_new_allocated_blocks > 0));
235     }
236 
generation(_Tp * ptr)237     inline int generation(_Tp *ptr) const
238     { return GCBlock::get(ptr)->generation; }
239 
head()240     inline GCBlock *head() const
241     { return m_head; }
242 
sweep(int generation)243     void sweep(int generation)
244     {
245         m_sweeping = true;
246         GCBlock *blk = m_head;
247         m_current = 0;
248 
249         m_new_allocated_blocks = 0;
250         m_new_allocated_extra_bytes = 0;
251 
252         while (blk != 0) {
253             if (blk->generation != generation) {
254                 if (m_current)
255                     m_current->next = blk->next;
256 
257                 GCBlock *tmp = blk;
258                 blk = blk->next;    // advance the pointer
259 
260                 tmp->next = m_free; // prepend the node to the free list...
261                 m_free = tmp;
262                 ++m_free_blocks;
263 
264                 if (m_free == m_head)
265                     m_head = blk;
266 
267                 _Tp *data = reinterpret_cast<_Tp *>(tmp->data());
268                 data->finalize();
269                 tmp->~GCBlock();
270             } else {
271                 m_current = blk;
272                 blk = blk->next;
273             }
274         }
275 
276         if (! m_current)
277             m_head = m_current;
278         m_sweeping = false;
279     }
280 
281     class const_iterator
282     {
283     public:
284         typedef _Tp value_type;
285         typedef const _Tp *pointer;
286         typedef const _Tp &reference;
const_iterator()287         inline const_iterator() : i(0) { }
const_iterator(GCBlock * block)288         inline const_iterator(GCBlock *block) : i(block) { }
const_iterator(const const_iterator & o)289         inline const_iterator(const const_iterator &o)
290         { i = reinterpret_cast<const const_iterator &>(o).i; }
291 
data()292         inline const _Tp *data() const { return reinterpret_cast<_Tp*>(i->data()); }
value()293         inline const _Tp &value() const { return *reinterpret_cast<_Tp*>(i->data()); }
294         inline const _Tp &operator*() const { return *reinterpret_cast<_Tp*>(i->data()); }
295         inline const _Tp *operator->() const { return reinterpret_cast<_Tp*>(i->data()); }
296         inline bool operator==(const const_iterator &o) const { return i == o.i; }
297         inline bool operator!=(const const_iterator &o) const { return i != o.i; }
298 
299         inline const_iterator &operator++() {
300             i = i->next;
301             return *this;
302         }
303     private:
304         GCBlock *i;
305     };
306     friend class const_iterator;
307 
constBegin()308     inline const_iterator constBegin() const { return const_iterator(m_head); }
constEnd()309     inline const_iterator constEnd() const { return const_iterator(0); }
310 
311 private:
312     Q_DISABLE_COPY(GCAlloc)
313 };
314 
315 } // namespace QScript
316 
317 QT_END_NAMESPACE
318 
319 #endif // QSCRIPT_GC_H
320