StackArena.cpp (4733B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "StackArena.h" 8 9 #include "mozilla/gfx/NumericTools.h" 10 #include "nsDebug.h" 11 12 namespace mozilla { 13 14 // A block of memory that the stack will chop up and hand out. 15 struct StackBlock { 16 // Subtract sizeof(StackBlock*) to give space for the |mNext| field. 17 static const size_t MAX_USABLE_SIZE = 4096 - sizeof(StackBlock*); 18 19 // A block of memory. 20 char mBlock[MAX_USABLE_SIZE]; 21 22 // Another block of memory that would only be created if our stack 23 // overflowed. 24 StackBlock* mNext; 25 26 StackBlock() : mNext(nullptr) {} 27 ~StackBlock() = default; 28 }; 29 30 static_assert(sizeof(StackBlock) == 4096, "StackBlock must be 4096 bytes"); 31 32 // We hold an array of marks. A push pushes a mark on the stack. 33 // A pop pops it off. 34 struct StackMark { 35 // The block of memory from which we are currently handing out chunks. 36 StackBlock* mBlock; 37 38 // Our current position in the block. 39 size_t mPos; 40 }; 41 42 StackArena* AutoStackArena::gStackArena; 43 44 StackArena::StackArena() { 45 mMarkLength = 0; 46 mMarks = nullptr; 47 48 // Allocate our stack memory. 49 mBlocks = new StackBlock(); 50 mCurBlock = mBlocks; 51 52 mStackTop = 0; 53 mPos = 0; 54 } 55 56 StackArena::~StackArena() { 57 // Free up our data. 58 delete[] mMarks; 59 while (mBlocks) { 60 StackBlock* toDelete = mBlocks; 61 mBlocks = mBlocks->mNext; 62 delete toDelete; 63 } 64 } 65 66 size_t StackArena::SizeOfExcludingThis( 67 mozilla::MallocSizeOf aMallocSizeOf) const { 68 size_t n = 0; 69 StackBlock* block = mBlocks; 70 while (block) { 71 n += aMallocSizeOf(block); 72 block = block->mNext; 73 } 74 n += aMallocSizeOf(mMarks); 75 return n; 76 } 77 78 static const int STACK_ARENA_MARK_INCREMENT = 50; 79 80 void StackArena::Push() { 81 // Resize the mark array if we overrun it. Failure to allocate the 82 // mark array is not fatal; we just won't free to that mark. This 83 // allows callers not to worry about error checking. 84 if (mStackTop >= mMarkLength) { 85 uint32_t newLength = mStackTop + STACK_ARENA_MARK_INCREMENT; 86 StackMark* newMarks = new StackMark[newLength]; 87 if (newMarks) { 88 if (mMarkLength) { 89 memcpy(newMarks, mMarks, sizeof(StackMark) * mMarkLength); 90 } 91 // Fill in any marks that we couldn't allocate during a prior call 92 // to Push(). 93 for (; mMarkLength < mStackTop; ++mMarkLength) { 94 MOZ_ASSERT_UNREACHABLE("should only hit this on out-of-memory"); 95 newMarks[mMarkLength].mBlock = mCurBlock; 96 newMarks[mMarkLength].mPos = mPos; 97 } 98 delete[] mMarks; 99 mMarks = newMarks; 100 mMarkLength = newLength; 101 } 102 } 103 104 // Set a mark at the top (if we can). 105 NS_ASSERTION(mStackTop < mMarkLength, "out of memory"); 106 if (mStackTop < mMarkLength) { 107 mMarks[mStackTop].mBlock = mCurBlock; 108 mMarks[mStackTop].mPos = mPos; 109 } 110 111 mStackTop++; 112 } 113 114 void* StackArena::Allocate(size_t aSize) { 115 NS_ASSERTION(mStackTop > 0, "Allocate called without Push"); 116 117 // Align to a multiple of 8. 118 aSize = RoundUpToMultiple(aSize, 8); 119 120 // On stack overflow, grab another block. 121 if (mPos + aSize >= StackBlock::MAX_USABLE_SIZE) { 122 NS_ASSERTION(aSize <= StackBlock::MAX_USABLE_SIZE, 123 "Requested memory is greater that our block size!!"); 124 if (mCurBlock->mNext == nullptr) { 125 mCurBlock->mNext = new StackBlock(); 126 } 127 128 mCurBlock = mCurBlock->mNext; 129 mPos = 0; 130 } 131 132 // Return the chunk they need. 133 void* result = mCurBlock->mBlock + mPos; 134 mPos += aSize; 135 136 return result; 137 } 138 139 void StackArena::Pop() { 140 // Pop off the mark. 141 NS_ASSERTION(mStackTop > 0, "unmatched pop"); 142 mStackTop--; 143 144 if (mStackTop >= mMarkLength) { 145 // We couldn't allocate the marks array at the time of the push, so 146 // we don't know where we're freeing to. 147 MOZ_ASSERT_UNREACHABLE("out of memory"); 148 if (mStackTop == 0) { 149 // But we do know if we've completely pushed the stack. 150 mCurBlock = mBlocks; 151 mPos = 0; 152 } 153 return; 154 } 155 156 #ifdef DEBUG 157 // Mark the "freed" memory with 0xdd to help with debugging of memory 158 // allocation problems. 159 { 160 StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock; 161 size_t pos = mMarks[mStackTop].mPos; 162 for (; block != block_end; block = block->mNext, pos = 0) { 163 memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos); 164 } 165 memset(block->mBlock + pos, 0xdd, mPos - pos); 166 } 167 #endif 168 169 mCurBlock = mMarks[mStackTop].mBlock; 170 mPos = mMarks[mStackTop].mPos; 171 } 172 173 } // namespace mozilla