Skip to content

Instantly share code, notes, and snippets.

@Helios-vmg
Created December 16, 2017 11:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Helios-vmg/382077403922655f215bc9526abb0883 to your computer and use it in GitHub Desktop.
Save Helios-vmg/382077403922655f215bc9526abb0883 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <boost/filesystem.hpp>
unsigned next_id;
#define SANITY_CHECK
class GcObject{
public:
unsigned id;
bool is_super;
bool allocated;
unsigned times_freed;
unsigned ref_count;
unsigned invalid_accesses;
std::vector<GcObject *> references;
enum class NodeState{
Undefined,
RefsCounted,
VisibilityConfirmed,
VisibilityTransferred,
AddedToFinalList,
Unlinked,
Freed,
};
NodeState freeing_state;
uintptr_t aux;
bool visible_from_outside;
template <typename F1, typename F2>
void traverse_reachable(GcObject *initial, std::vector<GcObject *> &stack, F1 &condition, F2 &operation){
stack.clear();
stack.push_back(initial);
while (stack.size()){
GcObject *obj = stack.back();
stack.pop_back();
if (!obj || !condition(obj))
continue;
operation(obj);
for (auto p : obj->references)
stack.push_back(p);
}
}
#define TR traverse_reachable
void count_internal_references(std::vector<GcObject *> &stack){
TR(
this,
stack,
[](GcObject *p){
p->aux++;
return p->freeing_state != NodeState::RefsCounted;
},
[](GcObject *p){
p->freeing_state = NodeState::RefsCounted;
p->aux = 1;
}
);
this->aux--;
}
GcObject *list_visible_nodes(std::vector<GcObject *> &stack){
GcObject *head = nullptr;
TR(
this,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::VisibilityConfirmed; },
[&](GcObject *p){
p->freeing_state = NodeState::VisibilityConfirmed;
if (p->visible_from_outside = (p->aux != p->ref_count)){
p->aux = (uintptr_t)head;
head = p;
}
}
);
return head;
}
void transfer_visibilities(GcObject *list, std::vector<GcObject *> &stack){
for (auto node = list; node; node = (GcObject *)node->aux){
TR(
node,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::VisibilityTransferred; },
[](GcObject *p){
p->freeing_state = NodeState::VisibilityTransferred;
p->visible_from_outside = 1;
}
);
}
}
std::vector<GcObject *> step4(std::vector<GcObject *> &stack){
std::vector<GcObject *> freeable;
TR(
this,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::AddedToFinalList; },
[&](GcObject *p){
p->freeing_state = NodeState::AddedToFinalList;
if (!p->visible_from_outside)
freeable.push_back(p);
}
);
return freeable;
}
GcObject *step4b(std::vector<GcObject *> &stack){
GcObject *head = nullptr;
TR(
this,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::AddedToFinalList; },
[&](GcObject *p){
p->freeing_state = NodeState::AddedToFinalList;
if (!p->visible_from_outside){
p->aux = (uintptr_t)head;
head = p;
}
}
);
return head;
}
void full_collection(std::vector<GcObject *> &stack){
TR(
this,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::Unlinked; },
[](GcObject *p){
p->freeing_state = NodeState::Unlinked;
p->unlink();
}
);
TR(
this,
stack,
[](GcObject *p){ return p->freeing_state != NodeState::Freed; },
[](GcObject *p){
p->freeing_state = NodeState::Freed;
p->free_resources();
}
);
}
void gced_free2(){
std::vector<GcObject *> stack;
this->count_internal_references(stack);
{
auto list = this->list_visible_nodes(stack);
if (!list){
this->full_collection(stack);
return;
}
this->transfer_visibilities(list, stack);
}
auto to_be_released = this->step4(stack);
for (auto p : to_be_released)
p->unlink();
for (auto p : to_be_released)
p->free_resources();
}
void gced_free3(){
std::vector<GcObject *> stack;
this->count_internal_references(stack);
auto list = this->list_visible_nodes(stack);
if (!list){
this->full_collection(stack);
return;
}
this->transfer_visibilities(list, stack);
list = this->step4b(stack);
for (auto node = list; node; node = (GcObject *)node->aux)
node->unlink();
for (auto node = list; node; node = (GcObject *)node->aux)
node->free_resources();
}
void unlink(){
for (auto p : this->references){
if (!p)
continue;
#ifdef SANITY_CHECK
if (!p->allocated)
this->invalid_accesses++;
#endif
p->non_recursive_unref();
}
}
void free_resources(){
#ifdef SANITY_CHECK
this->allocated = 0;
this->times_freed++;
#endif
this->ref_count = 0;
}
virtual void free(){
if (this->is_super){
return;
}
for (auto p : this->references){
if (!p)
continue;
#ifdef SANITY_CHECK
if (!p->allocated)
this->invalid_accesses++;
#endif
p->unref();
}
this->free_resources();
}
void non_recursive_unref(){
--this->ref_count;
}
public:
GcObject():
id(next_id++),
is_super(0),
allocated(1),
ref_count(0),
times_freed(0),
invalid_accesses(0),
freeing_state(NodeState::Undefined){}
void mark_super(){
this->is_super = 1;
}
void ref(){
this->ref_count++;
}
void unref(){
this->ref_count--;
if (this->is_super)
this->gced_free3();
else if (!this->ref_count)
this->free();
}
bool is_allocated() const{
return this->allocated;
}
void add_reference(GcObject *ref){
this->references.push_back(ref);
if (ref)
ref->ref();
}
unsigned get_times_freed() const{
return this->times_freed;
}
unsigned get_invalid_accesses() const{
return this->invalid_accesses;
}
};
typedef boost::filesystem::wpath path_t;
std::ofstream output;
void get_file_list(std::list<GcObject> &objects, boost::filesystem::directory_iterator i){
typedef boost::filesystem::directory_iterator DirIt;
DirIt e;
auto &up = objects.back();
for (; i != e; ++i){
objects.push_back(GcObject());
auto &New = objects.back();
output
<<New.id<<' '<<up.id<<std::endl
<<up.id<<' '<<New.id<<std::endl;
New.add_reference(&up);
up.add_reference(&New);
path_t path = *i;
try{
//std::cout <<path<<std::endl;
bool dir = boost::filesystem::is_directory(path);
if (dir)
get_file_list(objects, DirIt(path));
}catch (...){}
}
}
void get_file_list(std::list<GcObject> &objects){
boost::filesystem::directory_iterator it(L"\\");
objects.clear();
objects.resize(1);
objects.back().add_reference(nullptr);
output.open("output.txt");
get_file_list(objects, it);
output.close();
exit(0);
}
template <typename T>
void analyze_results(const T &objects, int test_case_no, unsigned expected_objects){
unsigned remaining_objects = 0;
unsigned incorrect_deallocations = 0;
unsigned invalid_accesses = 0;
for (auto &o : objects){
if (o.is_allocated())
remaining_objects++;
incorrect_deallocations += o.get_times_freed() > 1;
invalid_accesses += o.get_invalid_accesses();
}
std::cout <<"\nTest case "<<test_case_no<<".\n";
if (remaining_objects == expected_objects)
std::cout <<"OK\n";
else
std::cout <<"Objects remaining (should be "<<expected_objects<<"): "<<remaining_objects<<std::endl;
if (!incorrect_deallocations)
std::cout <<"OK\n";
else
std::cout <<"There were "<<incorrect_deallocations<<" incorrect deallocations.\n";
if (!invalid_accesses)
std::cout <<"OK\n";
else
std::cout <<"There were "<<invalid_accesses<<" invalid accesses.\n";
}
#define REF(x, y) objectsp[x].add_reference(objectsp + (y))
std::vector<std::pair<unsigned, unsigned> > load_graph(){
std::vector<std::pair<unsigned, unsigned> > ret;
std::ifstream input("output.txt");
while (1){
std::string line;
std::getline(input, line);
if (!input)
break;
std::stringstream stream(line);
unsigned a, b;
stream >>a >>b;
ret.push_back(std::make_pair(a, b));
}
return ret;
}
std::vector<GcObject> build_graph(std::vector<std::pair<unsigned, unsigned> > &pairs){
unsigned max = 0;
std::for_each(pairs.begin(), pairs.end(), [&](const std::pair<unsigned, unsigned> &pair){
max = std::max(max, pair.first);
max = std::max(max, pair.second);
});
std::vector<GcObject> ret;
ret.resize(max + 1);
auto objectsp = &ret[0];
for (auto &p : pairs){
objectsp[p.first].add_reference(objectsp + p.second);
}
return ret;
}
int main(){
#if 0
{
std::vector<std::pair<unsigned, unsigned> > pairs;
{
std::ifstream input("output2.txt");
while (1){
std::string line;
std::getline(input, line);
if (!input)
break;
std::stringstream stream(line);
unsigned a, b;
stream >>a >>b;
pairs.push_back(std::make_pair(a, b));
}
}
std::map<unsigned, std::list<unsigned> > map;
for (auto &p : pairs){
auto it = map.find(p.first);
map[p.first].push_back(p.second);
}
{
std::ofstream output("output3.txt");
output <<"graph {\n";
for (auto &p : map){
if (p.second.size() > 1){
output <<'n'<<p.first<<" -- { ";
bool skip = 1;
unsigned limit = 10;
for (auto n : p.second){
if (skip){
skip = 0;
continue;
}
output <<'n' <<n <<' ';
if (!--limit)
break;
}
output <<"};\n";
}
}
output <<"}\n";
}
return 0;
}
#endif
srand(time(nullptr));
#if 1
//test case 1
//stack->Super->A->B->A
{
next_id = 0;
std::vector<GcObject> objects(2);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 0);
objects[0].unref();
analyze_results(objects, 1, 0);
}
//test case 2
//stack->Super->A->B->C->B
{
next_id = 0;
std::vector<GcObject> objects(3);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 1);
objects[0].unref();
analyze_results(objects, 2, 0);
}
//test case 3
//stack->Super->A->B->C->B
// B->D->B
// D->E
{
next_id = 0;
std::vector<GcObject> objects(5);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 1);
REF(1, 3);
REF(3, 1);
REF(3, 4);
objects[0].unref();
analyze_results(objects, 3, 0);
}
//test case 4
//stack->Super->A->B->C->B
// B->D->B
// D->E
// B->E
{
next_id = 0;
std::vector<GcObject> objects(5);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 1);
REF(1, 3);
REF(3, 1);
REF(3, 4);
REF(1, 4);
objects[0].unref();
analyze_results(objects, 4, 0);
}
//test case 5
//stack->Super->A->B->C->B
// B->D->B
// D->E
// B->E
//stack->E
//test case 6
//stack->E
{
next_id = 0;
std::vector<GcObject> objects(5);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 1);
REF(1, 3);
REF(3, 1);
REF(3, 4);
REF(1, 4);
objects[4].ref();
objects[0].unref();
analyze_results(objects, 5, 1);
objects[4].unref();
analyze_results(objects, 6, 0);
}
#if 0
//stack->Super->A->B->C->B
// B->D->B
// D->E
// B->E->B
//stack->E
//test case 7
//stack->E
{
next_id = 0;
std::vector<GcObject> objects(5);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 1);
REF(1, 3);
REF(3, 1);
REF(3, 4);
REF(1, 4);
REF(4, 1);
objects[4].ref();
objects[0].unref();
objects[4].unref();
analyze_results(objects, 7, 0);
}
#endif
//test case 8
//test case 9
{
next_id = 0;
std::vector<GcObject> objects(10);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 1);
REF(1, 2);
REF(2, 3);
REF(3, 1);
REF(3, 4);
REF(4, 2);
REF(4, 0);
objects[5].ref();
objects[5].mark_super();
REF(5, 6);
REF(5, 9);
REF(6, 7);
REF(7, 8);
REF(8, 6);
REF(8, 9);
REF(9, 7);
REF(0, 9);
objects[5].unref();
analyze_results(objects, 8, 9);
objects[0].unref();
analyze_results(objects, 9, 0);
}
#endif
#if 1
//test case 10: randomized graph without cycles, with graph slicing
{
next_id = 0;
std::vector<GcObject> objects(1 << 20);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
for (size_t i = 0; i < objects.size() - 1; i++)
REF(i, i + 1);
for (unsigned i = 1 << 22; i--; ){
unsigned n1 = rand() | (rand() << 15);
n1 %= objects.size() - 1;
unsigned n2 = rand() | (rand() << 15);
auto max = objects.size();
auto min = n1 + 1;
n2 = min + n2 % (max - min);
REF(n1, n2);
}
unsigned n = rand() | (rand() << 15);
n %= objects.size();
objects[n].ref();
objects[n].mark_super();
clock_t t0 = clock();
objects[0].unref();
clock_t t1 = clock();
analyze_results(objects, 10, 0);
std::cout <<t1 - t0<<std::endl;
t0 = clock();
objects[n].unref();
t1 = clock();
analyze_results(objects, 11, 0);
std::cout <<t1 - t0<<std::endl;
}
//test case 12: randomized graph
{
next_id = 0;
std::vector<GcObject> objects(1 << 20);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
for (size_t i = 0; i < objects.size() - 1; i++)
REF(i, i + 1);
for (unsigned i = 1 << 22; i--; ){
unsigned n1 = rand() | (rand() << 15);
n1 %= objects.size();
unsigned n2 = rand() | (rand() << 15);
n2 %= objects.size();
REF(n1, n2);
}
unsigned n = rand() | (rand() << 15);
n %= objects.size();
//objects[n].ref();
objects[n].mark_super();
clock_t t0 = clock();
objects[0].unref();
clock_t t1 = clock();
analyze_results(objects, 12, 0);
std::cout <<t1 - t0<<std::endl;
//t0 = clock();
//objects[n].unref();
//t1 = clock();
//analyze_results(objects, 13, 0);
//std::cout <<t1 - t0<<std::endl;
}
#endif
#if 0
//test case 14: self-reference
{
next_id = 0;
std::vector<GcObject> objects(1);
auto objectsp = &objects[0];
objects[0].ref();
objects[0].mark_super();
REF(0, 0);
objects[0].unref();
analyze_results(objects, 14, 0);
}
#endif
auto graph_description = load_graph();
//test case 15: realistic graph, no slicing
{
next_id = 0;
auto objects = build_graph(graph_description);
objects.front().ref();
objects.front().mark_super();
clock_t t0 = clock();
objects.front().unref();
clock_t t1 = clock();
analyze_results(objects, 14, 0);
std::cout <<t1 - t0<<std::endl;
}
//test case 15: realistic graph with slicing
{
next_id = 0;
auto objects = build_graph(graph_description);
objects.front().ref();
objects.front().mark_super();
GcObject *o;
{
int j = objects.size() / 2;
o = &objects[1];
}
o->ref();
o->mark_super();
o->references.front()->unref();
o->references.front() = nullptr;
{
clock_t t0 = clock();
objects.front().unref();
clock_t t1 = clock();
analyze_results(objects, 15, 0);
std::cout <<t1 - t0<<std::endl;
}
{
clock_t t0 = clock();
o->unref();
clock_t t1 = clock();
analyze_results(objects, 16, 0);
std::cout <<t1 - t0<<std::endl;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment