src/Heap.cpp
author David Anderson <dvander@alliedmods.net>
Sat Jul 28 22:03:38 2012 -0700 (2012-07-28)
changeset 121 06bf112c6944
parent 91 5f4402180204
child 240 e3c851a14f16
permissions -rw-r--r--
trigger build
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of SourcePawn.
     6  *
     7  * SourcePawn is free software: you can redistribute it and/or modify it under
     8  * the terms of the GNU General Public License as published by the Free
     9  * Software Foundation, either version 3 of the License, or (at your option)
    10  * any later version.
    11  * 
    12  * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
    13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along with
    17  * SourcePawn. If not, see http://www.gnu.org/licenses/.
    18  */
    19 #include "Heap.h"
    20 #include "Heap-inl.h"
    21 #include "Spaces-inl.h"
    22 #include "VirtualObjectVisitor.h"
    23 #include "ObjectVisitor.h"
    24 #include "Zone.h"
    25 #include "StringPool.h"
    26 
    27 using namespace ke;
    28 
    29 static const size_t kMarkStackSize = 256 * kKB;
    30 
    31 Heap::Heap(Zone *zone)
    32   : zone_(zone),
    33     mapSpace_(zone),
    34     oldSpace_(zone),
    35     markStack_(NULL)
    36 {
    37 }
    38 
    39 bool
    40 Heap::initialize()
    41 {
    42     if (!lifoSpace_.initialize())
    43         return false;
    44     if (!oldSpace_.initialize())
    45         return false;
    46     if (!mapSpace_.initialize())
    47         return false;
    48     if (!largeSpace_.initialize())
    49         return false;
    50 
    51     markStack_ = malloc(kMarkStackSize);
    52     if (!markStack_)
    53         return false;
    54 
    55     if ((metaMap_ = allocateMap(MapKind_Meta, sizeof(Map))) == NULL)
    56         return false;
    57     metaMap_->init(metaMap_);
    58 
    59     return true;
    60 }
    61 
    62 Heap::~Heap()
    63 {
    64     free(markStack_);
    65 }
    66 
    67 Map *
    68 Heap::allocateMap(MapKind kind, size_t bytes)
    69 {
    70     size_t actualBytes = Align(bytes, kObjectAlignment);
    71 
    72     Address addr = mapSpace_.allocate(actualBytes);
    73     if (!addr) {
    74         reportOutOfMemory();
    75         return NULL;
    76     }
    77 
    78     // Note that we use a NULL type initially, and reinterpret-cast to get
    79     // the Type pointer, so we can allocate the self-referencing meta type.
    80     Object *obj = InitAddressIntoObject(addr, NULL);
    81     Map *map = reinterpret_cast<Map *>(obj);
    82     map->setKind(kind);
    83     return map;
    84 }
    85 
    86 // To avoid marking recursively, we use a worklist to manage the marking
    87 // process. Algorithm:
    88 //
    89 // (1) While the mark stack is not empty:
    90 //  (a) Remove object |o| from the mark stack.
    91 //  (b) For each reference |p| in object |o|,
    92 //   (i)  Mark |p| as Black.
    93 //   (ii) Push |p| onto the mark stack.
    94 //   (iv) If the mark stack overflowde, mark |p| as Grey.
    95 //
    96 // We run this algorithm after pushing every root object onto the mark stack.
    97 // After all roots have been processed, it may be that the mark stack
    98 // overflowed. In this case, we use the following algorithm:
    99 //
   100 //  (1) while the mark stack has overflowed:
   101 //   (a) Search the heap for grey objects, adding as many as possible to the
   102 //       mark stack.
   103 //   (b) Run the mark-stack-emptying algorithm above.
   104 //
   105 // This algorithm is guaranteed to terminate, because even if there are more
   106 // grey objects than mark stack capacity, we eventually all objects will
   107 // become grey, and grey objects are not re-pushed onto the mark stack. Thus,
   108 // eventually, all objects will become black.
   109 //
   110 namespace ke {
   111 class MarkStack
   112 {
   113     Object **base_;
   114     Object **top_;
   115     Object **end_;
   116     bool overflowed_;
   117 
   118   public:
   119     MarkStack(Object **base, Object **end)
   120       : base_(base),
   121         top_(base),
   122         end_(end),
   123         overflowed_(false)
   124     {
   125     }
   126 
   127     void pushBlack(Object *ptr) {
   128         assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(ptr)));
   129         if (top_ >= end_) {
   130             MarkBit bit = HeapChunk::GetMarkBit(ptr);
   131             HeapChunk::BlackToGrey(bit);
   132             overflowed_ = true;
   133             return;
   134         }
   135         *top_++ = ptr;
   136     }
   137 
   138     Object *pop() {
   139         assert(!empty());
   140         return *--top_;
   141     }
   142     bool overflowed() const {
   143         return overflowed_;
   144     }
   145     bool empty() const {
   146         return base_ == top_;
   147     }
   148     void reset() {
   149         assert(empty());
   150         overflowed_ = true;
   151     }
   152 };
   153 } // namespace ke
   154 
   155 // Iterates over an object's body, pushing its referenced objects onto the
   156 // mark stack.
   157 class MarkingVisitor : public ObjectVisitor<VirtualObjectVisitor>
   158 {
   159     MarkStack &stack_;
   160 
   161   public:
   162     MarkingVisitor(MarkStack &stack)
   163       : stack_(stack)
   164     {
   165     }
   166 
   167     void visit(Object **ptr) {
   168         if (!*ptr)
   169             return;
   170 
   171         Object *obj = *ptr;
   172 
   173         // If the object is grey, we'll find it later when searching the heap.
   174         // If the object is black, it's already been scsanned.
   175         MarkBit bit = HeapChunk::GetMarkBit(obj);
   176         if (HeapChunk::IsBlackOrGrey(bit))
   177             return;
   178 
   179         // Place the object in the stack and optimistically mark it black. If
   180         // the stack overflows, it'll become grey instead.
   181         HeapChunk::WhiteToBlack(bit);
   182         stack_.pushBlack(obj);
   183     }
   184 };
   185 
   186 static void
   187 ProcessMarkStack(MarkStack &stack)
   188 {
   189     MarkingVisitor visitor(stack);
   190 
   191     while (!stack.empty()) {
   192         Object *obj = stack.pop();
   193         assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(obj)));
   194 
   195         visitor.iterate(obj);
   196     }
   197 }
   198 
   199 class RootMarkingVisitor : public VirtualObjectVisitor
   200 {
   201     MarkStack &stack_;
   202     MarkingVisitor visitor_;
   203 
   204   public:
   205     RootMarkingVisitor(MarkStack &stack)
   206       : stack_(stack),
   207         visitor_(stack)
   208     {
   209     }
   210 
   211     void visit(Object **ptr) {
   212         // Flush the stack after every new root. Note that the root gets pushed
   213         // as black; that's okay. If the mark stack overflows, its children are
   214         // guaranteed to be at least grey.
   215         visitor_.visit(ptr);
   216         ProcessMarkStack(stack_);
   217     }
   218 };
   219 
   220 typedef ChunkObjectIteratorBase<ObjectSizeEvaluator> ChunkObjectIterator;
   221 
   222 static inline bool
   223 EnqueueGreyObject(MarkStack &stack, Object *obj)
   224 {
   225     assert(!stack.overflowed());
   226     MarkBit bit = HeapChunk::GetMarkBit(obj);
   227     if (HeapChunk::IsGrey(bit)) {
   228         HeapChunk::GreyToBlack(bit);
   229         stack.pushBlack(obj);
   230         return !stack.overflowed();
   231     }
   232     return true;
   233 }
   234 
   235 static void
   236 EnqueueGreyObjectsInChunks(MarkStack &stack, ChunkObjectIterator iter)
   237 {
   238     for (Object *obj = iter.next(); obj; obj = iter.next()) {
   239         if (!EnqueueGreyObject(stack, obj))
   240             return;
   241     }
   242 }
   243 
   244 static void
   245 EnqueueGreyObjectsInLargeChunks(MarkStack &stack, HeapChunk *first)
   246 {
   247     for (HeapChunk *chunk = first; chunk; chunk = chunk->next()) {
   248         Object *obj = reinterpret_cast<Object *>(chunk->first());
   249         if (!EnqueueGreyObject(stack, obj))
   250             return;
   251     }
   252 }
   253 
   254 void
   255 Heap::findGreyObjects(MarkStack &stack)
   256 {
   257     EnqueueGreyObjectsInChunks(stack, lifoSpace_.begin(ObjectSizeEvaluator()));
   258     if (stack.overflowed())
   259         return;
   260 
   261     EnqueueGreyObjectsInChunks(stack, mapSpace_.begin(ObjectSizeEvaluator()));
   262     if (stack.overflowed())
   263         return;
   264     
   265     EnqueueGreyObjectsInChunks(stack, oldSpace_.begin(ObjectSizeEvaluator()));
   266     if (stack.overflowed())
   267         return;
   268 
   269     EnqueueGreyObjectsInLargeChunks(stack, largeSpace_.first());
   270     if (stack.overflowed())
   271         return;
   272 }
   273 
   274 void
   275 Heap::mark()
   276 {
   277     Object **qbase = reinterpret_cast<Object **>(markStack_);
   278     Object **qend = qbase + (kMarkStackSize / sizeof(Object *));
   279     MarkStack stack(qbase, qend);
   280     RootMarkingVisitor visitor(stack);
   281 
   282     // Attempt to mark all objects reachable from the root set.
   283     zone_->accept(&visitor);
   284     assert(stack.empty());
   285 
   286     // If the stack overflowed, we need to look through the entire heap for
   287     // grey objects to rescan. We keep doing this until there are no grey
   288     // objects left.
   289     while (stack.overflowed()) {
   290         stack.reset();
   291         findGreyObjects(stack);
   292         ProcessMarkStack(stack);
   293     }
   294 }
   295 
   296 // Use the mark bits to scan for live objects, and mark the regions in
   297 // between them as free.
   298 static void
   299 SweepOldSpaceChunk(OldSpace *space, HeapChunk *chunk)
   300 {
   301     uint32 *markbits = chunk->markBits();
   302     uint32 *markbitsEnd = markbits + HeapChunk::kMarkWords;
   303     Address freeStart = chunk->first();
   304 
   305     // Scoot up to the first usable markword. Since memory is reserved for
   306     // mark bits, not all addresses are allocatable, so quite a few of the
   307     // initial mark bits are wasted. (1KB of mark bytes on x86.)
   308     markbits = HeapChunk::GetMarkBit(reinterpret_cast<Object *>(chunk->first())).word();
   309 
   310     uint32 bits = *markbits;
   311     for (;;) {
   312         while (bits) {
   313             uint32 bit = FindRightmostBit(bits);
   314             Address object = HeapChunk::FromMarkBit(chunk, markbits, bit);
   315 
   316             assert(object >= freeStart);
   317             if (freeStart != object)
   318                 space->markRegionFree(freeStart, object - freeStart);
   319 
   320             Object *obj = reinterpret_cast<Object *>(object);
   321             freeStart = object + SizeOfObject(obj, obj->map());
   322 
   323             // Since black is 10, we never need to clear the next bit.
   324             bits &= ~(1 << bit);
   325         }
   326         markbits++;
   327         bits = *markbits;
   328         if (markbits >= markbitsEnd)
   329             break;
   330     }
   331 
   332     if (freeStart == chunk->base())
   333         chunk->setEmpty();
   334     else if (freeStart != chunk->end())
   335         space->markRegionFree(freeStart, chunk->end() - freeStart);
   336 }
   337 
   338 static void
   339 SweepOldSpace(OldSpace *space)
   340 {
   341     space->resetFreeList();
   342     for (HeapChunk *chunk = space->first(); chunk; chunk = chunk->next()) {
   343         SweepOldSpaceChunk(space, chunk);
   344         chunk->resetMarkBits();
   345     }
   346     space->freeUnusedPages();
   347 }
   348 
   349 void
   350 Heap::sweep()
   351 {
   352     // Before destroying mark bits, sweep weak roots.
   353     zone_->symbols()->sweep();
   354 
   355     // Note: we don't bother sweeping the LIFO space.
   356     SweepOldSpace(&mapSpace_);
   357     SweepOldSpace(&oldSpace_);
   358     largeSpace_.sweep();
   359 }
   360 
   361 void
   362 Heap::reclaim()
   363 {
   364     mark();
   365     sweep();
   366 }
   367 
   368 void
   369 Heap::reportOutOfMemory()
   370 {
   371     zone_->reportOutOfMemory();
   372 }