Gnash  0.8.10
GC.h
Go to the documentation of this file.
00001 // GC.h: Garbage Collector for Gnash
00002 // 
00003 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
00004 //   Free Software Foundation, Inc
00005 // 
00006 // This program is free software; you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation; either version 3 of the License, or
00009 // (at your option) any later version.
00010 // 
00011 // This program is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 // 
00016 // You should have received a copy of the GNU General Public License
00017 // along with this program; if not, write to the Free Software
00018 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019 
00020 #ifndef GNASH_GC_H
00021 #define GNASH_GC_H
00022 
00023 // Define the following macro to enable GC verbosity 
00024 // Verbosity levels:
00025 //   1 - print stats about how many resources are registered and how many 
00026 //       are deleted, everytime the GC collector runs.
00027 //   2 - print a message for every GcResource being registered and being deleted
00028 //   3 - print info about the mark scan 
00029 //   
00030 //#define GNASH_GC_DEBUG 1
00031 
00032 #include <list>
00033 #include <map>
00034 #include <string>
00035 #include <cassert>
00036 
00037 #include "dsodefs.h"
00038 #ifdef GNASH_GC_DEBUG
00039 # include "log.h"
00040 # include "utility.h"
00041 #endif
00042 
00043 // Forward declarations.
00044 namespace gnash {
00045     class GC;
00046 }
00047 
00048 namespace gnash {
00049 
00051 //
00055 class GcRoot
00056 {
00057 public:
00058 
00060     //
00066     virtual void markReachableResources() const = 0;
00067 
00068     virtual ~GcRoot() {}
00069 };
00070 
00072 //
00074 class GcResource
00075 {
00076 public:
00077 
00078     friend class GC;
00079 
00081     //
00083     GcResource(GC& gc);
00084 
00086     //
00089     //
00092     void setReachable() const {
00093 
00094         if (_reachable) {
00095 
00096 #if GNASH_GC_DEBUG > 2
00097             log_debug(_("Instance %p of class %s already reachable, "
00098                     "setReachable doing nothing"), (void*)this,
00099                     typeName(*this));
00100 #endif
00101             return;
00102         }
00103 
00104 #if GNASH_GC_DEBUG  > 2
00105         log_debug(_("Instance %p of class %s set to reachable, scanning "
00106                 "reachable resources from it"), (void*)this,
00107                 typeName(*this));
00108 #endif
00109 
00110         _reachable = true;
00111         markReachableResources();
00112     }
00113 
00115     bool isReachable() const { return _reachable; }
00116 
00118     void clearReachable() const { _reachable = false; }
00119 
00120 protected:
00121 
00123     //
00136     virtual void markReachableResources() const {
00137         assert(_reachable);
00138 #if GNASH_GC_DEBUG > 1
00139         log_debug(_("Class %s didn't override the markReachableResources() "
00140                     "method"), typeName(*this));
00141 #endif
00142     }
00143 
00145     //
00149     virtual ~GcResource() {}
00150 
00151 private:
00152 
00153     mutable bool _reachable;
00154 
00155 };
00156 
00158 //
00164 class DSOEXPORT GC
00165 {
00166 
00167 public:
00168 
00170     //
00173     GC(GcRoot& root);
00174 
00176     ~GC();
00177 
00179     //
00194     void addCollectable(const GcResource* item) {
00195 
00196 #ifndef NDEBUG
00197         assert(item);
00198         assert(!item->isReachable());
00199 #endif
00200 
00201         _resList.push_back(item); ++_resListSize;
00202 
00203 #if GNASH_GC_DEBUG > 1
00204         log_debug(_("GC: collectable %p added, num collectables: %d"), item, 
00205                 _resListSize);
00206 #endif
00207     }
00208 
00210     void fuzzyCollect() {
00211 
00212         // Heuristic to decide wheter or not to run the collection cycle
00213         //
00214         //
00215         // Things to consider:
00216         //
00217         //  - Cost 
00218         //      - Depends on the number of reachable collectables
00219         //      - Depends on the frequency of runs
00220         //
00221         //  - Advantages 
00222         //      - Depends on the number of unreachable collectables
00223         //
00224         //  - Cheaply computable informations
00225         //      - Number of collectables (currently O(n) but can be optimized)
00226         //      - Total heap-allocated memory (currently unavailable)
00227         //
00228         // Current heuristic:
00229         //
00230         //  - We run the cycle again if X new collectables were allocated
00231         //    since last cycle run. X defaults to maxNewCollectablesCount
00232         //    and can be changed by user (GNASH_GC_TRIGGER_THRESHOLD env
00233         //    variable).
00234         //
00235         // Possible improvements:
00236         //
00237         //  - Adapt X (maxNewCollectablesCount) based on cost/advantage
00238         //    runtime analisys
00239         //
00240 
00241         if (_resListSize <  _lastResCount + _maxNewCollectablesCount) {
00242 #if GNASH_GC_DEBUG  > 1
00243             log_debug(_("GC: collection cycle skipped - %d/%d new resources "
00244                         "allocated since last run (from %d to %d)"),
00245                     _resListSize-_lastResCount, _maxNewCollectablesCount,
00246                     _lastResCount, _resListSize);
00247 #endif // GNASH_GC_DEBUG
00248             return;
00249         }
00250 
00251         runCycle();
00252     }
00253 
00255     //
00258     void runCycle();
00259 
00260     typedef std::map<std::string, unsigned int> CollectablesCount;
00261 
00263     void countCollectables(CollectablesCount& count) const;
00264 
00265 private:
00266 
00268     typedef std::list<const GcResource*> ResList;
00269 
00271     void markReachable() {
00272 #if GNASH_GC_DEBUG > 2
00273         log_debug(_("GC %p: MARK SCAN"), (void*)this);
00274 #endif
00275         _root.markReachableResources();
00276     }
00277 
00279     //
00281     size_t cleanUnreachable();
00282 
00285     size_t _maxNewCollectablesCount;
00286 
00288     ResList _resList;
00289 
00291     ResList::size_type _resListSize;
00292 
00294     GcRoot& _root;
00295 
00298     ResList::size_type _lastResCount;
00299 
00300 #ifdef GNASH_GC_DEBUG 
00301 
00302     size_t _collectorRuns;
00303 #endif
00304 };
00305 
00306 
00307 inline GcResource::GcResource(GC& gc)
00308     :
00309     _reachable(false)
00310 {
00311     gc.addCollectable(this);
00312 }
00313 
00314 } // namespace gnash
00315 
00316 #endif // GNASH_GC_H