File indexing completed on 2024-05-12 15:43:15
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 0004 * Copyright (C) 2001 Peter Kelly (pmk@post.com) 0005 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. 0006 * Copyright (C) 2007 Maksim Orlovich (maksim@kde.org) 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Lesser General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Lesser General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Lesser General Public 0019 * License along with this library; if not, write to the Free Software 0020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0021 * 0022 */ 0023 0024 #ifndef KJSCOLLECTOR_H_ 0025 #define KJSCOLLECTOR_H_ 0026 0027 #include <wtf/HashCountedSet.h> 0028 #include <cstring> 0029 #include <cstddef> 0030 0031 #define KJS_MEM_LIMIT 500000 0032 0033 namespace KJS 0034 { 0035 class JSCell; 0036 class JSValue; 0037 class CollectorBlock; 0038 0039 /** 0040 * @short Garbage collector. 0041 */ 0042 class KJS_EXPORT Collector 0043 { 0044 // disallow direct construction/destruction 0045 Collector(); 0046 public: 0047 /** 0048 * Register an object with the collector. The following assumptions are 0049 * made: 0050 * @li the operator new() of the object class is overloaded. 0051 * @li operator delete() has been overloaded as well and does not free 0052 * the memory on its own. 0053 * 0054 * @param s Size of the memory to be registered. 0055 * @return A pointer to the allocated memory. 0056 */ 0057 static void *allocate(size_t s); 0058 /** 0059 * Run the garbage collection. This involves calling the delete operator 0060 * on each object and freeing the used memory. 0061 */ 0062 static bool collect(); 0063 0064 static const size_t minExtraCostSize = 256; 0065 0066 static void reportExtraMemoryCost(size_t cost); 0067 0068 static size_t size(); 0069 static bool isOutOfMemory() 0070 { 0071 return memoryFull; 0072 } 0073 0074 #ifdef KJS_DEBUG_MEM 0075 /** 0076 * Check that nothing is left when the last interpreter gets deleted 0077 */ 0078 static void finalCheck(); 0079 #endif 0080 0081 static void protect(JSValue *); 0082 static void unprotect(JSValue *); 0083 0084 static size_t numInterpreters(); 0085 static size_t numProtectedObjects(); 0086 static HashCountedSet<const char *> *rootObjectTypeCounts(); 0087 0088 class Thread; 0089 static void registerThread(); 0090 0091 static bool isCellMarked(const JSCell *); 0092 static void markCell(JSCell *); 0093 0094 private: 0095 static const CollectorBlock *cellBlock(const JSCell *); 0096 static CollectorBlock *cellBlock(JSCell *); 0097 static size_t cellOffset(const JSCell *); 0098 0099 static void recordExtraCost(size_t); 0100 static void markProtectedObjects(); 0101 static void markCurrentThreadConservatively(); 0102 static void markOtherThreadConservatively(Thread *); 0103 static void markStackObjectsConservatively(); 0104 static void markStackObjectsConservatively(void *start, void *end); 0105 0106 static bool memoryFull; 0107 static void reportOutOfMemoryToAllInterpreters(); 0108 }; 0109 0110 // tunable parameters 0111 template<size_t bytesPerWord> struct CellSize; 0112 0113 // cell size needs to be a power of two for certain optimizations in collector.cpp 0114 // below also assume it's divisible by 8, and that block size is divisible by it 0115 template<> struct CellSize<4> { 0116 static const size_t m_value = 32; 0117 }; // 32-bit 0118 template<> struct CellSize<8> { 0119 static const size_t m_value = 64; 0120 }; // 64-bit 0121 const size_t BLOCK_SIZE = 16 * 4096; // 64k 0122 0123 const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; 0124 const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; 0125 0126 const size_t CELL_SIZE = CellSize<sizeof(void *)>::m_value; 0127 const size_t CELL_ARRAY_LENGTH = (CELL_SIZE / sizeof(double)); 0128 const size_t CELL_MASK = CELL_SIZE - 1; 0129 0130 // For each block, we can have at /most/ BLOCK_SIZE/CELL_SIZE entries. 0131 // Sice the bitmap accordingly, don't try to be fancy 0132 const size_t BITMAP_SIZE = (BLOCK_SIZE / CELL_SIZE + 7) / 8; 0133 const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); 0134 0135 // In each block, we have 3 bitmaps (mark for all blocks, extension + allocd for oversize cell blocks), 0136 // as well as an int and a pointer 0137 const size_t BLOCK_METADATA_SIZE = 3 * 4 * BITMAP_WORDS + sizeof(uint32_t) + sizeof(void *); 0138 const size_t CELLS_PER_BLOCK = (BLOCK_SIZE - BLOCK_METADATA_SIZE) / CELL_SIZE; 0139 0140 struct CollectorBitmap { 0141 uint32_t bits[BITMAP_WORDS]; 0142 bool get(size_t n) const 0143 { 0144 return !!(bits[n >> 5] & (1 << (n & 0x1F))); 0145 } 0146 void set(size_t n) 0147 { 0148 bits[n >> 5] |= (1 << (n & 0x1F)); 0149 } 0150 void clear(size_t n) 0151 { 0152 bits[n >> 5] &= ~(1 << (n & 0x1F)); 0153 } 0154 void clearAll() 0155 { 0156 std::memset(bits, 0, sizeof(bits)); 0157 } 0158 }; 0159 0160 struct CollectorCell { 0161 union { 0162 double memory[CELL_ARRAY_LENGTH]; 0163 struct { 0164 void *zeroIfFree; 0165 ptrdiff_t next; 0166 } freeCell; 0167 } u; 0168 }; 0169 0170 class CollectorBlock 0171 { 0172 public: 0173 CollectorCell cells[CELLS_PER_BLOCK]; 0174 uint32_t usedCells; 0175 CollectorCell *freeList; 0176 CollectorBitmap marked; 0177 CollectorBitmap allocd; 0178 CollectorBitmap trailer; 0179 }; 0180 0181 inline const CollectorBlock *Collector::cellBlock(const JSCell *cell) 0182 { 0183 return reinterpret_cast<const CollectorBlock *>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); 0184 } 0185 0186 inline CollectorBlock *Collector::cellBlock(JSCell *cell) 0187 { 0188 return const_cast<CollectorBlock *>(cellBlock(const_cast<const JSCell *>(cell))); 0189 } 0190 0191 inline size_t Collector::cellOffset(const JSCell *cell) 0192 { 0193 return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; 0194 } 0195 0196 inline bool Collector::isCellMarked(const JSCell *cell) 0197 { 0198 return cellBlock(cell)->marked.get(cellOffset(cell)); 0199 } 0200 0201 inline void Collector::markCell(JSCell *cell) 0202 { 0203 cellBlock(cell)->marked.set(cellOffset(cell)); 0204 } 0205 0206 inline void Collector::reportExtraMemoryCost(size_t cost) 0207 { 0208 if (cost > minExtraCostSize) { 0209 recordExtraCost(cost / (CELL_SIZE * 2)); 0210 } 0211 } 0212 } 0213 0214 #endif /* _KJSCOLLECTOR_H_ */