summaryrefslogtreecommitdiff
path: root/cpp/src/IceUtil/GC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceUtil/GC.cpp')
-rw-r--r--cpp/src/IceUtil/GC.cpp225
1 files changed, 225 insertions, 0 deletions
diff --git a/cpp/src/IceUtil/GC.cpp b/cpp/src/IceUtil/GC.cpp
new file mode 100644
index 00000000000..693da31a61c
--- /dev/null
+++ b/cpp/src/IceUtil/GC.cpp
@@ -0,0 +1,225 @@
+// **********************************************************************
+//
+// Copyright (c) 2003
+// ZeroC, Inc.
+// Billerica, MA, USA
+//
+// All Rights Reserved.
+//
+// Ice is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License version 2 as published by
+// the Free Software Foundation.
+//
+// **********************************************************************
+
+#include <IceUtil/GC.h>
+#include <IceUtil/Time.h>
+#include <IceUtil/StaticRecMutex.h>
+#include <map>
+
+using namespace std;
+
+int IceUtil::GC::_numCollectors = 0;
+
+IceUtil::GC::GC(int interval, StatsCallback cb)
+{
+ if(_numCollectors++ > 0)
+ {
+ abort(); // Enforce singleton.
+ }
+
+ Monitor<Mutex>::Lock sync(*this);
+ _running = false;
+ _collecting = false;
+ _interval = interval;
+ _statsCallback = cb;
+}
+
+IceUtil::GC::~GC()
+{
+ Monitor<Mutex>::Lock sync(*this);
+
+ --_numCollectors;
+}
+
+void
+IceUtil::GC::run()
+{
+ {
+ Monitor<Mutex>::Lock sync(*this);
+ if(_interval == 0)
+ {
+ return;
+ }
+ _running = true;
+ }
+
+ while(true)
+ {
+ bool collect = false;
+ {
+ Time waitTime = Time::seconds(_interval);
+ Monitor<Mutex>::Lock sync(*this);
+ if(!_running)
+ {
+ return;
+ }
+ if(!timedWait(waitTime))
+ {
+ collect = true;
+ }
+ }
+ if(collect)
+ {
+ collectGarbage();
+ }
+ }
+}
+
+void
+IceUtil::GC::stop()
+{
+ {
+ Monitor<Mutex>::Lock sync(*this);
+ if(!_running)
+ {
+ return;
+ }
+ }
+ {
+ Monitor<Mutex>::Lock sync(*this);
+ _running = false;
+ notify();
+ }
+ getThreadControl().join();
+}
+
+void
+IceUtil::GC::collectGarbage()
+{
+ {
+ Monitor<Mutex>::Lock sync(*this);
+
+ if(_collecting)
+ {
+ return;
+ }
+ _collecting = true;
+ }
+
+ typedef map<ObjectBase*, int> ObjectCounts;
+ ObjectCounts counts;
+
+ Time t;
+
+ GCStats stats;
+
+ StaticRecMutex::Lock sync(gcMutex);
+
+ if(_statsCallback)
+ {
+ t = Time::now();
+ }
+
+ //
+ // Initialize a set of pairs <ObjectBase*, ref count> for all class instances.
+ //
+ {
+ for(ObjectSet::const_iterator i = objects.begin(); i != objects.end(); ++i)
+ {
+ assert(*i);
+ counts[*i] = (*i)->_ref;
+ }
+ }
+
+ if(_statsCallback)
+ {
+ stats.examined = counts.size();
+ stats.collected = 0;
+ }
+
+ //
+ // For each class instance in the set, find which class instances can be reached from that instance.
+ // For each reachable instance, decrement the reachable instance's ref count.
+ //
+ {
+ for(ObjectSet::const_iterator i = objects.begin(); i != objects.end(); ++i)
+ {
+ ObjectMultiSet reachable;
+ (*i)->__gcReachable(reachable);
+ for(ObjectMultiSet::const_iterator j = reachable.begin(); j != reachable.end(); ++j)
+ {
+ --(counts.find(*j)->second);
+ }
+ }
+ }
+
+ //
+ // Any instances with a ref count > 0 are referenced from outside the set of class instances (and therefore
+ // reachable from the program, for example, via Ptr variable on the stack). Remove these reachable instances
+ // (and all instances reachable from them) from the overall set of objects.
+ //
+ {
+ ObjectSet reachable;
+ for(ObjectCounts::const_iterator i = counts.begin(); i != counts.end(); ++i)
+ {
+ if(i->second > 0)
+ {
+ recursivelyReachable(i->first, reachable);
+ }
+ }
+
+ for(ObjectSet::const_iterator k = reachable.begin(); k != reachable.end(); ++k)
+ {
+ counts.erase(*k);
+ }
+ }
+
+ //
+ // What is left in the counts set can be garbage collected.
+ //
+ {
+ ObjectCounts::const_iterator i;
+ for(i = counts.begin(); i != counts.end(); ++i)
+ {
+ i->first->__gcClear();
+ }
+ for(i = counts.begin(); i != counts.end(); ++i)
+ {
+ delete i->first;
+ if(_statsCallback)
+ {
+ ++stats.collected;
+ }
+ }
+ counts.clear();
+ }
+
+ if(_statsCallback)
+ {
+ stats.msec = (Time::now() - t) * 1000.0L;
+ _statsCallback(stats);
+ }
+
+ {
+ Monitor<Mutex>::Lock sync(*this);
+
+ _collecting = false;
+ }
+}
+
+void
+IceUtil::GC::recursivelyReachable(ObjectBase* p, ObjectSet& o)
+{
+ if(o.find(p) == o.end())
+ {
+ assert(p);
+ o.insert(p);
+ ObjectMultiSet tmp;
+ p->__gcReachable(tmp);
+ for(ObjectMultiSet::const_iterator i = tmp.begin(); i != tmp.end(); ++i)
+ {
+ recursivelyReachable(*i, o);
+ }
+ }
+}