LCOV - app.info - js/src/ds/LifoAlloc.h

LCOV - code coverage report
| Current view: | directory - js/src/ds - LifoAlloc.h (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 121 | 106 | 87.6 %
| Date: | 2012-04-07 | Functions: | 80 | 77 | 96.2 %

           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

Generated by: LCOV version 1.7