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 434236664 : 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 434236664 : char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1));
72 434236664 : JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
73 434236664 : 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 217034952 : char *headerBase() { return reinterpret_cast<char *>(this); }
85 449940554 : char *bumpBase() const { return limit - bumpSpaceSize; }
86 :
87 382654 : BumpChunk *thisDuringConstruction() { return this; }
88 :
89 382654 : explicit BumpChunk(size_t bumpSpaceSize)
90 382654 : : bump(reinterpret_cast<char *>(thisDuringConstruction()) + sizeof(BumpChunk)),
91 382654 : limit(bump + bumpSpaceSize),
92 765308 : next_(NULL), bumpSpaceSize(bumpSpaceSize)
93 : {
94 382654 : JS_ASSERT(bump == AlignPtr(bump));
95 382654 : }
96 :
97 220780644 : void setBump(void *ptr) {
98 220780644 : JS_ASSERT(bumpBase() <= ptr);
99 220780644 : JS_ASSERT(ptr <= limit);
100 441561288 : DebugOnly<char *> prevBump = bump;
101 220780644 : bump = static_cast<char *>(ptr);
102 : #ifdef DEBUG
103 220780644 : JS_ASSERT(contains(prevBump));
104 :
105 : /* Clobber the now-free space. */
106 220780644 : if (prevBump > bump)
107 4633221 : memset(bump, 0xcd, prevBump - bump);
108 : #endif
109 220780644 : }
110 :
111 : public:
112 1874029 : BumpChunk *next() const { return next_; }
113 313251 : void setNext(BumpChunk *succ) { next_ = succ; }
114 :
115 23908 : size_t used() const { return bump - bumpBase(); }
116 908 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) {
117 908 : return mallocSizeOf(this);
118 : }
119 :
120 455895 : void resetBump() {
121 455895 : setBump(headerBase() + sizeof(BumpChunk));
122 455895 : }
123 :
124 4177679 : void *mark() const { return bump; }
125 :
126 4177679 : void release(void *mark) {
127 4177679 : JS_ASSERT(contains(mark));
128 4177679 : JS_ASSERT(mark <= bump);
129 4177679 : setBump(mark);
130 4177679 : }
131 :
132 229136002 : bool contains(void *mark) const {
133 229136002 : 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 216892299 : void *tryAlloc(size_t n) {
141 216892299 : char *aligned = AlignPtr(bump);
142 216892299 : char *newBump = aligned + n;
143 :
144 216892299 : if (newBump > limit)
145 745229 : return NULL;
146 :
147 : /* Check for overflow. */
148 216147070 : if (JS_UNLIKELY(newBump < bump))
149 0 : return NULL;
150 :
151 216147070 : JS_ASSERT(canAlloc(n)); /* Ensure consistency between "can" and "try". */
152 216147070 : setBump(newBump);
153 216147070 : return aligned;
154 : }
155 :
156 814641 : void *allocInfallible(size_t n) {
157 814641 : void *result = tryAlloc(n);
158 814641 : JS_ASSERT(result);
159 814641 : 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 325828 : void reset(size_t defaultChunkSize) {
196 325828 : JS_ASSERT(RoundUpPow2(defaultChunkSize) == defaultChunkSize);
197 325828 : first = latest = NULL;
198 325828 : defaultChunkSize_ = defaultChunkSize;
199 325828 : markCount = 0;
200 325828 : }
201 :
202 : public:
203 203386 : explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); }
204 :
205 : /* Steal allocated chunks from |other|. */
206 122442 : void steal(LifoAlloc *other) {
207 122442 : JS_ASSERT(!other->markCount);
208 122442 : PodCopy((char *) this, (char *) other, sizeof(*this));
209 122442 : other->reset(defaultChunkSize_);
210 122442 : }
211 :
212 187921 : ~LifoAlloc() { freeAll(); }
213 :
214 122442 : 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 216147070 : void *alloc(size_t n) {
224 216147070 : JS_OOM_POSSIBLY_FAIL();
225 :
226 : void *result;
227 216147070 : if (latest && (result = latest->tryAlloc(n)))
228 215332429 : return result;
229 :
230 814641 : if (!getOrCreateChunk(n))
231 0 : return NULL;
232 :
233 814641 : return latest->allocInfallible(n);
234 : }
235 :
236 : template <typename T>
237 6323962 : T *newArray(size_t count) {
238 6323962 : void *mem = alloc(sizeof(T) * count);
239 6323962 : if (!mem)
240 0 : return NULL;
241 : JS_STATIC_ASSERT(tl::IsPodType<T>::result);
242 6323962 : 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 2035386 : T *newArrayUninitialized(size_t count) {
251 2035386 : return static_cast<T *>(alloc(sizeof(T) * count));
252 : }
253 :
254 4201592 : void *mark() {
255 4201592 : markCount++;
256 :
257 4201592 : return latest ? latest->mark() : NULL;
258 : }
259 :
260 4201592 : void release(void *mark) {
261 4201592 : markCount--;
262 :
263 4201592 : if (!mark) {
264 23913 : latest = first;
265 23913 : if (latest)
266 23908 : latest->resetBump();
267 23913 : 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 4177679 : BumpChunk *container = first;
276 0 : while (true) {
277 4177679 : if (container->contains(mark))
278 : break;
279 0 : JS_ASSERT(container != latest);
280 0 : container = container->next();
281 : }
282 4177679 : latest = container;
283 4177679 : 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 12 : size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
299 12 : size_t accum = 0;
300 12 : BumpChunk *it = first;
301 932 : while (it) {
302 908 : accum += it->sizeOfIncludingThis(mallocSizeOf);
303 908 : it = it->next();
304 : }
305 12 : 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 980883 : T *newPod() {
317 980883 : return static_cast<T *>(alloc(sizeof(T)));
318 : }
319 :
320 142952794 : 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 4067321 : explicit LifoAllocScope(LifoAlloc *lifoAlloc
332 : JS_GUARD_OBJECT_NOTIFIER_PARAM)
333 4067321 : : lifoAlloc(lifoAlloc), shouldRelease(true) {
334 4067321 : JS_GUARD_OBJECT_NOTIFIER_INIT;
335 4067321 : mark = lifoAlloc->mark();
336 4067321 : }
337 :
338 8134642 : ~LifoAllocScope() {
339 4067321 : if (shouldRelease)
340 4067186 : lifoAlloc->release(mark);
341 4067321 : }
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
|