LCOV - code coverage report
Current view: directory - js/src/ds - LifoAlloc.h (source / functions) Found Hit Coverage
Test: app.info Lines: 121 115 95.0 %
Date: 2012-06-02 Functions: 81 80 98.8 %

       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

Generated by: LCOV version 1.7