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 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Leary <cdleary@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef LifoAlloc_h__
42 : #define LifoAlloc_h__
43 :
44 : #include "mozilla/Attributes.h"
45 :
46 : /*
47 : * This data structure supports stacky LIFO allocation (mark/release and
48 : * LifoAllocScope). It does not maintain one contiguous segment; instead, it
49 : * maintains a bunch of linked memory segments. In order to prevent malloc/free
50 : * thrashing, unused segments are deallocated when garbage collection occurs.
51 : */
52 :
53 : #include "jsutil.h"
54 :
55 : #include "js/TemplateLib.h"
56 :
57 : namespace js {
58 :
59 : namespace detail {
60 :
61 : static const size_t LIFO_ALLOC_ALIGN = 8;
62 :
63 : JS_ALWAYS_INLINE
64 : char *
65 64618357 : AlignPtr(void *orig)
66 : {
67 : typedef tl::StaticAssert<
68 : tl::FloorLog2<LIFO_ALLOC_ALIGN>::result == tl::CeilingLog2<LIFO_ALLOC_ALIGN>::result
69 : >::result _;
70 :
71 64618357 : char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1));
72 64618357 : JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
73 64618357 : return result;
74 : }
75 :
76 : /* Header for a chunk of memory wrangled by the LifoAlloc. */
77 : class BumpChunk
78 : {
79 : char *bump; /* start of the available data */
80 : char *limit; /* end of the data */
81 : BumpChunk *next_; /* the next BumpChunk */
82 : size_t bumpSpaceSize; /* size of the data area */
83 :
84 32241672 : char *headerBase() { return reinterpret_cast<char *>(this); }
85 77098739 : char *bumpBase() const { return limit - bumpSpaceSize; }
86 :
87 93652 : BumpChunk *thisDuringConstruction() { return this; }
88 :
89 93652 : explicit BumpChunk(size_t bumpSpaceSize)
90 93652 : : bump(reinterpret_cast<char *>(thisDuringConstruction()) + sizeof(BumpChunk)),
91 93652 : limit(bump + bumpSpaceSize),
92 187304 : next_(NULL), bumpSpaceSize(bumpSpaceSize)
93 : {
94 93652 : JS_ASSERT(bump == AlignPtr(bump));
95 93652 : }
96 :
97 35378948 : void setBump(void *ptr) {
98 35378948 : JS_ASSERT(bumpBase() <= ptr);
99 35378948 : JS_ASSERT(ptr <= limit);
100 70757896 : DebugOnly<char *> prevBump = bump;
101 35378948 : bump = static_cast<char *>(ptr);
102 : #ifdef DEBUG
103 35378948 : JS_ASSERT(contains(prevBump));
104 :
105 : /* Clobber the now-free space. */
106 35378948 : if (prevBump > bump)
107 3203241 : memset(bump, 0xcd, prevBump - bump);
108 : #endif
109 35378948 : }
110 :
111 : public:
112 207623 : BumpChunk *next() const { return next_; }
113 33680 : void setNext(BumpChunk *succ) { next_ = succ; }
114 :
115 19671 : size_t used() const { return bump - bumpBase(); }
116 0 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) {
117 0 : return mallocSizeOf(this);
118 : }
119 :
120 42981 : void resetBump() {
121 42981 : setBump(headerBase() + sizeof(BumpChunk));
122 42981 : }
123 :
124 3160586 : void *mark() const { return bump; }
125 :
126 3160586 : void release(void *mark) {
127 3160586 : JS_ASSERT(contains(mark));
128 3160586 : JS_ASSERT(mark <= bump);
129 3160586 : setBump(mark);
130 3160586 : }
131 :
132 41700120 : bool contains(void *mark) const {
133 41700120 : return bumpBase() <= mark && mark <= limit;
134 : }
135 :
136 : bool canAlloc(size_t n);
137 :
138 : /* Try to perform an allocation of size |n|, return null if not possible. */
139 : JS_ALWAYS_INLINE
140 32232362 : void *tryAlloc(size_t n) {
141 32232362 : char *aligned = AlignPtr(bump);
142 32232362 : char *newBump = aligned + n;
143 :
144 32232362 : if (newBump > limit)
145 56981 : return NULL;
146 :
147 : /* Check for overflow. */
148 32175381 : if (JS_UNLIKELY(newBump < bump))
149 0 : return NULL;
150 :
151 32175381 : JS_ASSERT(canAlloc(n)); /* Ensure consistency between "can" and "try". */
152 32175381 : setBump(newBump);
153 32175381 : return aligned;
154 : }
155 :
156 116962 : void *allocInfallible(size_t n) {
157 116962 : void *result = tryAlloc(n);
158 116962 : JS_ASSERT(result);
159 116962 : return result;
160 : }
161 :
162 : static BumpChunk *new_(size_t chunkSize);
163 : static void delete_(BumpChunk *chunk);
164 : };
165 :
166 : } /* namespace detail */
167 :
168 : /*
169 : * LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
170 : *
171 : * Note: |latest| is not necessary "last". We leave BumpChunks latent in the
172 : * chain after they've been released to avoid thrashing before a GC.
173 : */
174 : class LifoAlloc
175 : {
176 : typedef detail::BumpChunk BumpChunk;
177 :
178 : BumpChunk *first;
179 : BumpChunk *latest;
180 : size_t markCount;
181 : size_t defaultChunkSize_;
182 :
183 : void operator=(const LifoAlloc &) MOZ_DELETE;
184 : LifoAlloc(const LifoAlloc &) MOZ_DELETE;
185 :
186 : /*
187 : * Return a BumpChunk that can perform an allocation of at least size |n|
188 : * and add it to the chain appropriately.
189 : *
190 : * Side effect: if retval is non-null, |first| and |latest| are initialized
191 : * appropriately.
192 : */
193 : BumpChunk *getOrCreateChunk(size_t n);
194 :
195 240556 : void reset(size_t defaultChunkSize) {
196 240556 : JS_ASSERT(RoundUpPow2(defaultChunkSize) == defaultChunkSize);
197 240556 : first = latest = NULL;
198 240556 : defaultChunkSize_ = defaultChunkSize;
199 240556 : markCount = 0;
200 240556 : }
201 :
202 : public:
203 156879 : explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); }
204 :
205 : /* Steal allocated chunks from |other|. */
206 83677 : void steal(LifoAlloc *other) {
207 83677 : JS_ASSERT(!other->markCount);
208 83677 : PodCopy((char *) this, (char *) other, sizeof(*this));
209 83677 : other->reset(defaultChunkSize_);
210 83677 : }
211 :
212 143723 : ~LifoAlloc() { freeAll(); }
213 :
214 83677 : size_t defaultChunkSize() const { return defaultChunkSize_; }
215 :
216 : /* Frees all held memory. */
217 : void freeAll();
218 :
219 : /* Should be called on GC in order to release any held chunks. */
220 : void freeUnused();
221 :
222 : JS_ALWAYS_INLINE
223 32175381 : void *alloc(size_t n) {
224 32175381 : JS_OOM_POSSIBLY_FAIL();
225 :
226 : void *result;
227 32175381 : if (latest && (result = latest->tryAlloc(n)))
228 32058419 : return result;
229 :
230 116962 : if (!getOrCreateChunk(n))
231 0 : return NULL;
232 :
233 116962 : return latest->allocInfallible(n);
234 : }
235 :
236 : template <typename T>
237 4606248 : T *newArray(size_t count) {
238 4606248 : void *mem = alloc(sizeof(T) * count);
239 4606248 : if (!mem)
240 0 : return NULL;
241 : JS_STATIC_ASSERT(tl::IsPodType<T>::result);
242 4606248 : return (T *) mem;
243 : }
244 :
245 : /*
246 : * Create an array with uninitialized elements of type |T|.
247 : * The caller is responsible for initialization.
248 : */
249 : template <typename T>
250 2146269 : T *newArrayUninitialized(size_t count) {
251 2146269 : return static_cast<T *>(alloc(sizeof(T) * count));
252 : }
253 :
254 3180262 : void *mark() {
255 3180262 : markCount++;
256 :
257 3180262 : return latest ? latest->mark() : NULL;
258 : }
259 :
260 3180262 : void release(void *mark) {
261 3180262 : markCount--;
262 :
263 3180262 : if (!mark) {
264 19676 : latest = first;
265 19676 : if (latest)
266 19671 : latest->resetBump();
267 19676 : return;
268 : }
269 :
270 : /*
271 : * Find the chunk that contains |mark|, and make sure we don't pass
272 : * |latest| along the way -- we should be making the chain of active
273 : * chunks shorter, not longer!
274 : */
275 3160586 : BumpChunk *container = first;
276 0 : while (true) {
277 3160586 : if (container->contains(mark))
278 : break;
279 0 : JS_ASSERT(container != latest);
280 0 : container = container->next();
281 : }
282 3160586 : latest = container;
283 3160586 : latest->release(mark);
284 : }
285 :
286 : /* Get the total "used" (occupied bytes) count for the arena chunks. */
287 : size_t used() const {
288 : size_t accum = 0;
289 : BumpChunk *it = first;
290 : while (it) {
291 : accum += it->used();
292 : it = it->next();
293 : }
294 : return accum;
295 : }
296 :
297 : /* Get the total size of the arena chunks (including unused space). */
298 0 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
299 0 : size_t accum = 0;
300 0 : BumpChunk *it = first;
301 0 : while (it) {
302 0 : accum += it->sizeOfIncludingThis(mallocSizeOf);
303 0 : it = it->next();
304 : }
305 0 : return accum;
306 : }
307 :
308 : /* Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself. */
309 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
310 : return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
311 : }
312 :
313 : /* Doesn't perform construction; useful for lazily-initialized POD types. */
314 : template <typename T>
315 : JS_ALWAYS_INLINE
316 156778 : T *newPod() {
317 156778 : return static_cast<T *>(alloc(sizeof(T)));
318 : }
319 :
320 15624511 : JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE)
321 : };
322 :
323 : class LifoAllocScope
324 : {
325 : LifoAlloc *lifoAlloc;
326 : void *mark;
327 : bool shouldRelease;
328 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
329 :
330 : public:
331 3071068 : explicit LifoAllocScope(LifoAlloc *lifoAlloc
332 : JS_GUARD_OBJECT_NOTIFIER_PARAM)
333 3071068 : : lifoAlloc(lifoAlloc), shouldRelease(true) {
334 3071068 : JS_GUARD_OBJECT_NOTIFIER_INIT;
335 3071068 : mark = lifoAlloc->mark();
336 3071068 : }
337 :
338 6142136 : ~LifoAllocScope() {
339 3071068 : if (shouldRelease)
340 3070933 : lifoAlloc->release(mark);
341 3071068 : }
342 :
343 : LifoAlloc &alloc() {
344 : return *lifoAlloc;
345 : }
346 :
347 135 : void releaseEarly() {
348 135 : JS_ASSERT(shouldRelease);
349 135 : lifoAlloc->release(mark);
350 135 : shouldRelease = false;
351 135 : }
352 : };
353 :
354 : } /* namespace js */
355 :
356 : #endif
|