1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Copyright (C) 2010 Apple Inc. All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * 2. Redistributions in binary form must reproduce the above copyright
13 : * notice, this list of conditions and the following disclaimer in the
14 : * documentation and/or other materials provided with the distribution.
15 : *
16 : * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
17 : * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
20 : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 : * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : *
28 : * ***** END LICENSE BLOCK ***** */
29 :
30 : #ifndef BumpPointerAllocator_h
31 : #define BumpPointerAllocator_h
32 :
33 : #include "PageAllocation.h"
34 :
35 : namespace WTF {
36 :
37 : #if WTF_CPU_SPARC
38 : #define MINIMUM_BUMP_POOL_SIZE 0x2000
39 : #elif WTF_CPU_IA64
40 : #define MINIMUM_BUMP_POOL_SIZE 0x4000
41 : #else
42 : #define MINIMUM_BUMP_POOL_SIZE 0x1000
43 : #endif
44 :
45 : class BumpPointerPool {
46 : public:
47 : // ensureCapacity will check whether the current pool has capacity to
48 : // allocate 'size' bytes of memory If it does not, it will attempt to
49 : // allocate a new pool (which will be added to this one in a chain).
50 : //
51 : // If allocation fails (out of memory) this method will return null.
52 : // If the return value is non-null, then callers should update any
53 : // references they have to this current (possibly full) BumpPointerPool
54 : // to instead point to the newly returned BumpPointerPool.
55 4749033 : BumpPointerPool* ensureCapacity(size_t size)
56 : {
57 4749033 : void* allocationEnd = static_cast<char*>(m_current) + size;
58 4749033 : ASSERT(allocationEnd > m_current); // check for overflow
59 4749033 : if (allocationEnd <= static_cast<void*>(this))
60 4718001 : return this;
61 31032 : return ensureCapacityCrossPool(this, size);
62 : }
63 :
64 : // alloc should only be called after calling ensureCapacity; as such
65 : // alloc will never fail.
66 4749033 : void* alloc(size_t size)
67 : {
68 4749033 : void* current = m_current;
69 4749033 : void* allocationEnd = static_cast<char*>(current) + size;
70 4749033 : ASSERT(allocationEnd > current); // check for overflow
71 4749033 : ASSERT(allocationEnd <= static_cast<void*>(this));
72 4749033 : m_current = allocationEnd;
73 4749033 : return current;
74 : }
75 :
76 : // The dealloc method releases memory allocated using alloc. Memory
77 : // must be released in a LIFO fashion, e.g. if the client calls alloc
78 : // four times, returning pointer A, B, C, D, then the only valid order
79 : // in which these may be deallocaed is D, C, B, A.
80 : //
81 : // The client may optionally skip some deallocations. In the example
82 : // above, it would be valid to only explicitly dealloc C, A (D being
83 : // dealloced along with C, B along with A).
84 : //
85 : // If pointer was not allocated from this pool (or pools) then dealloc
86 : // will CRASH(). Callers should update any references they have to
87 : // this current BumpPointerPool to instead point to the returned
88 : // BumpPointerPool.
89 134449 : BumpPointerPool* dealloc(void* position)
90 : {
91 134449 : if ((position >= m_start) && (position <= static_cast<void*>(this))) {
92 134440 : ASSERT(position <= m_current);
93 134440 : m_current = position;
94 134440 : return this;
95 : }
96 9 : return deallocCrossPool(this, position);
97 : }
98 :
99 : private:
100 : // Placement operator new, returns the last 'size' bytes of allocation for use as this.
101 31359 : void* operator new(size_t size, const PageAllocation& allocation)
102 : {
103 31359 : ASSERT(size < allocation.size());
104 31359 : return reinterpret_cast<char*>(reinterpret_cast<intptr_t>(allocation.base()) + allocation.size()) - size;
105 : }
106 :
107 31359 : BumpPointerPool(const PageAllocation& allocation)
108 31359 : : m_current(allocation.base())
109 31359 : , m_start(allocation.base())
110 : , m_next(0)
111 : , m_previous(0)
112 62718 : , m_allocation(allocation)
113 : {
114 31359 : }
115 :
116 31359 : static BumpPointerPool* create(size_t minimumCapacity = 0)
117 : {
118 : // Add size of BumpPointerPool object, check for overflow.
119 31359 : minimumCapacity += sizeof(BumpPointerPool);
120 31359 : if (minimumCapacity < sizeof(BumpPointerPool))
121 0 : return 0;
122 :
123 31359 : size_t poolSize = MINIMUM_BUMP_POOL_SIZE;
124 62718 : while (poolSize < minimumCapacity) {
125 0 : poolSize <<= 1;
126 : // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2!
127 : ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1)));
128 0 : if (!poolSize)
129 0 : return 0;
130 : }
131 :
132 31359 : PageAllocation allocation = PageAllocation::allocate(poolSize);
133 31359 : if (!!allocation)
134 31359 : return new(allocation) BumpPointerPool(allocation);
135 0 : return 0;
136 : }
137 :
138 67716 : void shrink()
139 : {
140 67716 : ASSERT(!m_previous);
141 67716 : m_current = m_start;
142 166464 : while (m_next) {
143 31032 : BumpPointerPool* nextNext = m_next->m_next;
144 31032 : m_next->destroy();
145 31032 : m_next = nextNext;
146 : }
147 67716 : }
148 :
149 31359 : void destroy()
150 : {
151 31359 : m_allocation.deallocate();
152 31359 : }
153 :
154 31032 : static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size)
155 : {
156 : // The pool passed should not have capacity, so we'll start with the next one.
157 31032 : ASSERT(previousPool);
158 31032 : ASSERT((static_cast<char*>(previousPool->m_current) + size) > previousPool->m_current); // check for overflow
159 31032 : ASSERT((static_cast<char*>(previousPool->m_current) + size) > static_cast<void*>(previousPool));
160 31032 : BumpPointerPool* pool = previousPool->m_next;
161 :
162 0 : while (true) {
163 31032 : if (!pool) {
164 : // We've run to the end; allocate a new pool.
165 31032 : pool = BumpPointerPool::create(size);
166 31032 : previousPool->m_next = pool;
167 31032 : pool->m_previous = previousPool;
168 31032 : return pool;
169 : }
170 :
171 : //
172 0 : void* current = pool->m_current;
173 0 : void* allocationEnd = static_cast<char*>(current) + size;
174 0 : ASSERT(allocationEnd > current); // check for overflow
175 0 : if (allocationEnd <= static_cast<void*>(pool))
176 0 : return pool;
177 : }
178 : }
179 :
180 9 : static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position)
181 : {
182 : // Should only be called if position is not in the current pool.
183 9 : ASSERT((position < pool->m_start) || (position > static_cast<void*>(pool)));
184 :
185 31023 : while (true) {
186 : // Unwind the current pool to the start, move back in the chain to the previous pool.
187 31032 : pool->m_current = pool->m_start;
188 31032 : pool = pool->m_previous;
189 :
190 : // position was nowhere in the chain!
191 31032 : if (!pool)
192 0 : CRASH();
193 :
194 31032 : if ((position >= pool->m_start) && (position <= static_cast<void*>(pool))) {
195 9 : ASSERT(position <= pool->m_current);
196 9 : pool->m_current = position;
197 9 : return pool;
198 : }
199 : }
200 : }
201 :
202 : void* m_current;
203 : void* m_start;
204 : BumpPointerPool* m_next;
205 : BumpPointerPool* m_previous;
206 : PageAllocation m_allocation;
207 :
208 : friend class BumpPointerAllocator;
209 : };
210 :
211 : // A BumpPointerAllocator manages a set of BumpPointerPool objects, which
212 : // can be used for LIFO (stack like) allocation.
213 : //
214 : // To begin allocating using this class call startAllocator(). The result
215 : // of this method will be null if the initial pool allocation fails, or a
216 : // pointer to a BumpPointerPool object that can be used to perform
217 : // allocations. Whilst running no memory will be released until
218 : // stopAllocator() is called. At this point all allocations made through
219 : // this allocator will be reaped, and underlying memory may be freed.
220 : //
221 : // (In practice we will still hold on to the initial pool to allow allocation
222 : // to be quickly restared, but aditional pools will be freed).
223 : //
224 : // This allocator is non-renetrant, it is encumbant on the clients to ensure
225 : // startAllocator() is not called again until stopAllocator() has been called.
226 : class BumpPointerAllocator {
227 : public:
228 345 : BumpPointerAllocator()
229 345 : : m_head(0)
230 : {
231 345 : }
232 :
233 345 : ~BumpPointerAllocator()
234 : {
235 345 : if (m_head)
236 327 : m_head->destroy();
237 345 : }
238 :
239 67716 : BumpPointerPool* startAllocator()
240 : {
241 67716 : if (!m_head)
242 327 : m_head = BumpPointerPool::create();
243 67716 : return m_head;
244 : }
245 :
246 67716 : void stopAllocator()
247 : {
248 67716 : if (m_head)
249 67716 : m_head->shrink();
250 67716 : }
251 :
252 : private:
253 : BumpPointerPool* m_head;
254 : };
255 :
256 : }
257 :
258 : using WTF::BumpPointerAllocator;
259 :
260 : #endif // BumpPointerAllocator_h
|