Topic: Minor change allows improved garbage collection of shared_ptr
Author: "jwat" <jwaterloo@idvelocity.com>
Date: Fri, 30 Sep 2005 23:42:13 CST Raw View
Interesting things can be done if:
1) void sp_scalar_constructor_hook(void * px, std::size_t size, void *
pn, const type_info* rtti)
2) void sp_scalar_destructor_hook(void *, std::size_t, void * pn, const
type_info* rtti)
are given an additional parameter of 'const type_info* rtti'.
This would only require this minor change to the shared_count class.
template<class T> void cbi_call_constructor_hook(sp_counted_base * pn,
T * px, checked_deleter<T> const &, int)
{
boost::sp_scalar_constructor_hook(px, sizeof(T), pn, &typeid(T));
}
template<class T> void cbi_call_destructor_hook(sp_counted_base * pn, T
* px, checked_deleter<T> const &, int)
{
boost::sp_scalar_destructor_hook(px, sizeof(T), pn, &typeid(T));
}
The result is that now SEAL's Reflex, an awesome mature reflection
library for C++, can be combined with boost's shared_ptr and there
experimental sp_collector producing what I call sp_reflex_collector
that can garbage collect without any regard to the alignment problems
of sp_collector. Following is the code. It has even been optimized
somewhat to avoid doing excessive reflection on classes that have no
posssibly of cyclical references. That's right it is adaptive. Please
tell me your thoughts.
// sp_reflex_collector.cpp : Defines the entry point for the console
application.
//
#include "stdafx.h"
#include "Reflex/Reflex.h"
#include "Reflex/Builder/ReflexBuilder.h"
using namespace seal::reflex;
#define BOOST_SP_ENABLE_DEBUG_HOOKS
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
#include <boost/assert.hpp>
#include "friendlier_shared_ptr.hpp"
#include <boost/detail/lightweight_mutex.hpp>
#include <cstdlib>
#include <map>
#include <deque>
#include <iostream>
#include <boost/tuple/tuple.hpp>
typedef std::map< void const *, boost::tuple<void *, size_t, const
type_info*> > map_type;
struct XX
{
//void* fill[32];
char mess1;
char mess2;
//std::vector<char> mess;
boost::friendlier_shared_ptr<XX> p;
char mess3;
char mess4;
};
namespace { Type type_void = TypeBuilder("void");
Type type_12 = TypeBuilder("char");
Type type_13 = TypeBuilder("boost::friendlier_shared_ptr<XX>");
Type type_3 = TypeBuilder("XX");
Type type_3c = ConstBuilder(type_3);
Type type_14 = ReferenceBuilder(type_3c);
Type type_17 = PointerBuilder(type_3);
Type type_13c = ConstBuilder(type_13);
Type type_22 = ReferenceBuilder(type_13c);
}
//------Dictionary for class XX -------------------------------
class __XX_dict {
public:
__XX_dict();
static void * constructor_10(void*, const std::vector<void*>&,
void*);
static void * constructor_11(void*, const std::vector<void*>&,
void*);
};
__XX_dict::__XX_dict() {
ClassBuilder< ::XX >("XX", PUBLIC)
.addDataMember(type_12, "mess1", OffsetOf(::XX, mess1), PUBLIC)
.addDataMember(type_12, "mess2", OffsetOf(::XX, mess2), PUBLIC)
.addDataMember(type_13, "p", OffsetOf(::XX, p), PUBLIC)
.addDataMember(type_12, "mess3", OffsetOf(::XX, mess3), PUBLIC)
.addDataMember(type_12, "mess4", OffsetOf(::XX, mess4), PUBLIC)
.addFunctionMember(FunctionTypeBuilder(type_void, type_14), "XX",
constructor_10, 0, "_ctor_arg", PUBLIC | CONSTRUCTOR)
.addFunctionMember(FunctionTypeBuilder(type_void), "XX",
constructor_11, 0, 0, PUBLIC | CONSTRUCTOR);
}
//------Stub functions for class XX -------------------------------
void* __XX_dict::constructor_10( void* mem, const std::vector<void*>&
arg, void*) {
return ::new(mem) ::XX(*(const ::XX*)arg[0]);
}
void* __XX_dict::constructor_11( void* mem, const std::vector<void*>&,
void*) {
return ::new(mem) ::XX();
}
//------Dictionary for class friendlier_shared_ptr<XX>
-------------------------------
class __boost__friendlier_shared_ptr_XX__dict {
public:
__boost__friendlier_shared_ptr_XX__dict();
static void * constructor_19(void*, const std::vector<void*>&,
void*);
static void * constructor_20(void*, const std::vector<void*>&,
void*);
};
__boost__friendlier_shared_ptr_XX__dict::__boost__friendlier_shared_ptr_XX__dict()
{
ClassBuilder< ::boost::friendlier_shared_ptr<XX>
>("boost::friendlier_shared_ptr<XX>", PUBLIC)
.addTypedef(type_3, "boost::friendlier_shared_ptr<XX>::element_type")
.addTypedef(type_3, "boost::friendlier_shared_ptr<XX>::value_type")
.addTypedef(type_17, "boost::friendlier_shared_ptr<XX>::pointer")
.addFunctionMember(FunctionTypeBuilder(type_void, type_22),
"friendlier_shared_ptr", constructor_19, 0, "_ctor_arg", PUBLIC |
CONSTRUCTOR)
.addFunctionMember(FunctionTypeBuilder(type_void),
"friendlier_shared_ptr", constructor_20, 0, 0, PUBLIC | CONSTRUCTOR);
}
//------Stub functions for class friendlier_shared_ptr<XX>
-------------------------------
void* __boost__friendlier_shared_ptr_XX__dict::constructor_19( void*
mem, const std::vector<void*>& arg, void*) {
return ::new(mem) ::boost::friendlier_shared_ptr<XX>(*(const
::boost::friendlier_shared_ptr<XX>*)arg[0]);
}
void* __boost__friendlier_shared_ptr_XX__dict::constructor_20( void*
mem, const std::vector<void*>&, void*) {
return ::new(mem) ::boost::friendlier_shared_ptr<XX>();
}
namespace {
struct Dictionaries {
Dictionaries() {
NamespaceBuilder( "boost" );
__XX_dict();
__boost__friendlier_shared_ptr_XX__dict();
}
};
static Dictionaries instance;
}
static map_type & get_map()
{
static map_type m;
return m;
}
typedef boost::detail::lightweight_mutex mutex_type;
static mutex_type & get_mutex()
{
static mutex_type m;
return m;
}
static void * init_mutex_before_main = &get_mutex();
namespace
{
class X;
struct count_layout
{
boost::detail::sp_counted_base * pi;
int id;
};
struct shared_ptr_layout
{
X * px;
count_layout pn;
};
}
// assume 4 byte alignment for pointers when scanning
size_t const pointer_align = 4;
typedef std::map<void const *, long> map2_type;
TypeTemplate get_friendlier_shared_ptr_TypeTemplate()
{
static TypeTemplate tt;
if(tt)
return tt;
Scope s = Scope::byName("boost");
size_t ttc = s.typeTemplateCount();
for(size_t i = 0; i < ttc; i++)
{
TypeTemplate tempTT = s.typeTemplate(i);
if(tempTT.name() == "friendlier_shared_ptr")
{
tt = tempTT;
return tt;
}
}
return tt;
}
static void scan_and_count(void const * area, size_t size, map_type
const & m, map2_type & m2, const type_info* rtti)
{
TypeTemplate tt = get_friendlier_shared_ptr_TypeTemplate();
std::string potentialCyclical("potentialCyclical");
//Type spt =
Type::byTypeInfo(typeid(boost::friendlier_shared_ptr<XX>));
Type t = Type::byTypeInfo(*rtti);
PropertyList pl = t.propertyList();
size_t dmc = t.dataMemberCount();
if(pl.hasKey(potentialCyclical))
{
Any pval = pl.propertyValue(potentialCyclical);
bool potentialCyclical = any_cast<bool>(pval);
// optimization
// there is no need to do gc
// if it is impossible for the object
// to have cyclical references
if(potentialCyclical == false)
return;
}
else
{
bool potCyclical = false;
// determine if this type is potentially cyclical
for(size_t i = 0; i < dmc; i++)
{
Member m = t.dataMember(i);
Type mt = m.type();
if(mt.templateFamily() == tt)
{
potCyclical = true;
break;
}
}
Any potcyc(potCyclical);
pl.addProperty(potentialCyclical, potcyc);
// optimization
// there is no need to do gc
// if it is impossible for the object
// to have cyclical references
if(potCyclical == false)
return;
}
Object o(t, const_cast<void*>(area));
for(size_t i = 0; i < dmc; i++)
{
Member dm = t.dataMember(i);
Type mt = dm.type();
if(mt.templateFamily() != tt)// no need to graph non shared_ptr
members
continue;
Object field = dm.get(o);
shared_ptr_layout const * q =
reinterpret_cast<shared_ptr_layout const *>(field.address());
if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0
&& m.count(q->pn.pi) != 0)
++m2[q->pn.pi];
}
//unsigned char const * p = static_cast<unsigned char const
*>(area);
//for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p +=
pointer_align, n += pointer_align)
//{
// shared_ptr_layout const * q =
reinterpret_cast<shared_ptr_layout const *>(p);
// if(q->pn.id == boost::detail::shared_count_id && q->pn.pi !=
0 && m.count(q->pn.pi) != 0)
// {
// ++m2[q->pn.pi];
// }
//}
}
typedef std::deque<void const *> open_type;
static void scan_and_mark(void const * area, size_t size, map2_type &
m2, open_type & open, const type_info* rtti)
{
TypeTemplate tt = get_friendlier_shared_ptr_TypeTemplate();
std::string potentialCyclical("potentialCyclical");
//Type spt =
Type::byTypeInfo(typeid(boost::friendlier_shared_ptr<XX>));
Type t = Type::byTypeInfo(*rtti);
PropertyList pl = t.propertyList();
size_t dmc = t.dataMemberCount();
if(pl.hasKey(potentialCyclical))
{
Any pval = pl.propertyValue(potentialCyclical);
bool potentialCyclical = any_cast<bool>(pval);
// optimization
// there is no need to do gc
// if it is impossible for the object
// to have cyclical references
if(potentialCyclical == false)
return;
}
else
{
bool potCyclical = false;
// determine if this type is potentially cyclical
for(size_t i = 0; i < dmc; i++)
{
Member m = t.dataMember(i);
Type mt = m.type();
if(mt.templateFamily() == tt)
{
potCyclical = true;
break;
}
}
Any potcyc(potCyclical);
pl.addProperty(potentialCyclical, potcyc);
// optimization
// there is no need to do gc
// if it is impossible for the object
// to have cyclical references
if(potCyclical == false)
return;
}
Object o(t, const_cast<void*>(area));
for(size_t i = 0; i < dmc; i++)
{
Member dm = t.dataMember(i);
Type mt = dm.type();
if(mt.templateFamily() != tt)// no need to graph non shared_ptr
members
continue;
Object field = dm.get(o);
shared_ptr_layout const * q =
reinterpret_cast<shared_ptr_layout const *>(field.address());
if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0
&& m2.count(q->pn.pi) != 0)
{
open.push_back(q->pn.pi);
m2.erase(q->pn.pi);
}
}
//unsigned char const * p = static_cast<unsigned char const
*>(area);
//for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p +=
pointer_align, n += pointer_align)
//{
// shared_ptr_layout const * q =
reinterpret_cast<shared_ptr_layout const *>(p);
// if(q->pn.id == boost::detail::shared_count_id && q->pn.pi !=
0 && m2.count(q->pn.pi) != 0)
// {
// open.push_back(q->pn.pi);
// m2.erase(q->pn.pi);
// }
//}
}
static void find_unreachable_objects_impl(map_type const & m, map2_type
& m2)
{
// scan objects for friendlier_shared_ptr members, compute internal
counts
{
std::cout << "... " << (unsigned int)m.size() << " objects in
m.\n";
for(map_type::const_iterator i = m.begin(); i != m.end(); ++i)
{
boost::detail::sp_counted_base const * p =
static_cast<boost::detail::sp_counted_base const *>(i->first);
BOOST_ASSERT(p->use_count() != 0); // there should be no
inactive counts in the map
scan_and_count(i->second.get<0>(), i->second.get<1>(), m,
m2, i->second.get<2>());
}
std::cout << "... " << (unsigned int)m2.size() << " objects in
m2.\n";
}
// mark reachable objects
{
open_type open;
for(map2_type::iterator i = m2.begin(); i != m2.end(); ++i)
{
boost::detail::sp_counted_base const * p =
static_cast<boost::detail::sp_counted_base const *>(i->first);
if(p->use_count() != i->second) open.push_back(p);
}
std::cout << "... " << (unsigned int)m2.size() << " objects in
open.\n";
for(open_type::iterator j = open.begin(); j != open.end(); ++j)
{
m2.erase(*j);
}
while(!open.empty())
{
void const * p = open.front();
open.pop_front();
map_type::const_iterator i = m.find(p);
BOOST_ASSERT(i != m.end());
scan_and_mark(i->second.get<0>(), i->second.get<1>(), m2,
open, i->second.get<2>());
}
}
// m2 now contains the unreachable objects
}
std::size_t find_unreachable_objects(bool report)
{
map2_type m2;
#ifdef BOOST_HAS_THREADS
// This will work without the #ifdef, but some compilers warn
// that lock is not referenced
mutex_type::scoped_lock lock(get_mutex());
#endif
map_type const & m = get_map();
find_unreachable_objects_impl(m, m2);
if(report)
{
for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j)
{
map_type::const_iterator i = m.find(j->first);
BOOST_ASSERT(i != m.end());
std::cout << "Unreachable object at " << i->second.get<0>()
<< ", " << (unsigned int)i->second.get<1>() << " bytes long.\n";
}
}
return m2.size();
}
typedef std::deque< boost::friendlier_shared_ptr<X> > free_list_type;
static void scan_and_free(void * area, size_t size, map2_type const &
m2, free_list_type & free)
{
unsigned char * p = static_cast<unsigned char *>(area);
for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p +=
pointer_align, n += pointer_align)
{
shared_ptr_layout * q = reinterpret_cast<shared_ptr_layout
*>(p);
if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0
&& m2.count(q->pn.pi) != 0 && q->px != 0)
{
boost::friendlier_shared_ptr<X> * ppx = reinterpret_cast<
boost::friendlier_shared_ptr<X> * >(p);
free.push_back(*ppx);
ppx->reset();
}
}
}
void free_unreachable_objects()
{
free_list_type free;
{
map2_type m2;
#ifdef BOOST_HAS_THREADS
mutex_type::scoped_lock lock(get_mutex());
#endif
map_type const & m = get_map();
find_unreachable_objects_impl(m, m2);
for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j)
{
map_type::const_iterator i = m.find(j->first);
BOOST_ASSERT(i != m.end());
scan_and_free(i->second.get<0>(), i->second.get<1>(), m2,
free);
}
}
std::cout << "... about to free " << (unsigned int)free.size() << "
objects.\n";
}
// debug hooks
namespace boost
{
void sp_scalar_constructor_hook(void *)
{
}
void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn,
const type_info* rtti)
{
#ifdef BOOST_HAS_THREADS
mutex_type::scoped_lock lock(get_mutex());
#endif
get_map()[pn] = boost::make_tuple(px, size, rtti);
}
void sp_scalar_destructor_hook(void *)
{
}
void sp_scalar_destructor_hook(void *, std::size_t, void * pn, const
type_info* rtti)
{
#ifdef BOOST_HAS_THREADS
mutex_type::scoped_lock lock(get_mutex());
#endif
get_map().erase(pn);
}
void sp_array_constructor_hook(void *)
{
}
void sp_array_destructor_hook(void *)
{
}
} // namespace boost
#endif // defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
//#include <boost/friendlier_shared_ptr.hpp>
#include <vector>
//#include <iostream>
//#include <cstdlib>
#include <ctime>
//
//// sp_collector.cpp exported functions
//
//std::size_t find_unreachable_objects(bool report);
//void free_unreachable_objects();
//struct XX
//{
// //void* fill[32];
// char mess1;
// char mess2;
// //std::vector<char> mess;
// boost::friendlier_shared_ptr<XX> p;
// char mess3;
// char mess4;
//};
void report()
{
std::cout << "Calling find_unreachable_objects:\n";
std::clock_t t = std::clock();
std::size_t n = find_unreachable_objects(false);
t = std::clock() - t;
std::cout << (unsigned int)n << " unreachable objects.\n";
std::cout << " " << static_cast<double>(t) / CLOCKS_PER_SEC << "
seconds.\n";
}
void free()
{
std::cout << "Calling free_unreachable_objects:\n";
std::clock_t t = std::clock();
free_unreachable_objects();
t = std::clock() - t;
std::cout << " " << static_cast<double>(t) / CLOCKS_PER_SEC << "
seconds.\n";
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector< boost::friendlier_shared_ptr<XX> > v1, v2;
int const n = 256;// * 1024;
std::cout << "Filling v1 and v2\n";
for(int i = 0; i < n; ++i)
{
v1.push_back(boost::friendlier_shared_ptr<XX>(new XX));
v2.push_back(boost::friendlier_shared_ptr<XX>(new XX));
}
report();
std::cout << "Creating the cycles\n";
for(int i = 0; i < n - 1; ++i)
{
v2[i]->p = v2[i+1];
}
v2[n-1]->p = v2[0];
report();
std::cout << "Resizing v2 to size of 1\n";
v2.resize(1);
report();
std::cout << "Clearing v2\n";
v2.clear();
report();
std::cout << "Clearing v1\n";
v1.clear();
report();
free();
report();
return 0;
}
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]