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_ */