:(before "End Cell Fields")
int nrefs;
:(code)
void test_cell_layout() {
CHECK((sizeof(cell)%4) == 0);
}
:(code)
cell* mkref(cell* c) {
if (c == nil) return nil;
xlog << "mkref " << (void*)c << ": " << c << " " << c->nrefs << '\n';
++c->nrefs;
return c;
}
void rmref(cell* c) {
if (c == nil) return;
new_trace_frame("rmref");
if (c->nrefs <= 0) RAISE << c << " has " << c->nrefs << " nrefs\n";
xlog << "dec " << (void*)c << ": " << c << " " << c->nrefs << '\n';
--c->nrefs;
if (c->nrefs <= 0)
reclaim(c);
}
void rmref(list<cell*> l) {
for (list<cell*>::iterator p = l.begin(); p != l.end(); ++p)
rmref(*p);
}
void reclaim(cell* c) {
xlog << "reclaim " << (void*)c << ": " << c << " " << c->nrefs << '\n';
if (c->type == INTEGER || c->type == SYMBOL)
RAISE << "atom should be saved for reuse, never reclaimed: " << c << '\n';
switch (c->type) {
case INTEGER:
break;
case STRING:
case SYMBOL:
delete (string*)c->left; break;
case TREE:
rmref(c->left); break;
default:
RAISE << "Can't reclaim type " << c->type << '\n' << die();
return;
}
rmref(c->right);
free_cell(c);
}
^L
:(before "End Types")
struct lease_cell;
#define TEMP(var, cell_expr) cell* var = cell_expr; lease_cell lease_##var(var);
struct lease_cell {
cell*& value;
lease_cell(cell*& v);
~lease_cell();
};
:(code)
lease_cell::lease_cell(cell*& v) :value(v) {}
lease_cell::~lease_cell() { rmref(value); }
void update(cell*& var, cell* expr) {
rmref(var);
var = expr;
}
void test_lease_cell() {
TEMP(x, mkref(new_cell()));
}
void test_lease_cell_is_mutable() {
TEMP(x, nil);
x = mkref(new_cell());
}
cell* drop_ptr(cell* p) {
cell* x = mkref(right(p));
xlog << "drop_ptr: before " << (void*)x << ": " << x << " " << x->nrefs << '\n';
if (p->nrefs == 0) ++p->nrefs;
rmref(p);
xlog << "drop_ptr: after " << (void*)x << ": " << x << " " << x->nrefs << '\n';
return x;
}
void add_cell(cell* p, cell* x) {
set_right(p, new_cell(x));
}
^L
void init(cell* c) {
c->type = TREE;
c->left = c->right = nil;
c->nrefs = 0;
}
void clear(cell* c) {
c->type = TREE;
c->left = c->right = NULL;
c->nrefs = 0;
}
:(after "void rmref(cell* c")
if (!c) {
RAISE << "A cell was prematurely reclaimed.\n" << die();
return;
}
:(before "End Globals")
long Num_used_cells = 0;
:(code)
cell* new_cell() {
++Num_used_cells;
cell* result = new cell;
init(result);
xlog << "alloc " << (void*)result << '\n';
return result;
}
void free_cell(cell* c) {
--Num_used_cells;
trace("mem") << "free";
xlog << "free " << (void*)c << ": " << c << " " << c->nrefs << '\n';
clear(c);
delete c;
}
void test_new_cell_has_nil_left_and_right() {
TEMP(x, mkref(new_cell()));
CHECK_EQ(x->left, nil);
CHECK_EQ(x->right, nil);
}
void pending_test_freed_cell_has_null_left_and_right() {
cell* x = mkref(new_cell());
free_cell(x);
CHECK_EQ((void*)x->left, NULL);
CHECK_EQ((void*)x->right, NULL);
}
void test_rmref_frees_space() {
cell* c = mkref(new_cell());
rmref(c);
CHECK_EQ(trace_count("mem", "free"), 1);
}
:(before "End Test Teardown")
if (Passed && !Hide_warnings && trace_count("warn") == 0) {
if (Num_used_cells > 0)
RAISE << Num_used_cells << " leaked cells.\n";
continue;
}