1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

810 lines
23 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Guido Tack <guido.tack@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <minizinc/ast.hh>
#include <minizinc/config.hh>
#include <minizinc/gc.hh>
#include <minizinc/hash.hh>
#include <minizinc/model.hh>
#include <minizinc/support/dtrace.h>
#include <minizinc/timer.hh>
#include <cstring>
#include <vector>
//#define MINIZINC_GC_STATS
#if defined(MINIZINC_GC_STATS)
#include <map>
#endif
namespace MiniZinc {
GC*& GC::gc(void) {
#if defined(HAS_DECLSPEC_THREAD)
__declspec(thread) static GC* gc = NULL;
#elif defined(HAS_ATTR_THREAD)
static __thread GC* gc = NULL;
#else
#error Need thread-local storage
#endif
return gc;
}
bool GC::locked(void) {
assert(gc());
return gc()->_lock_count > 0;
}
GCLock::GCLock(void) { GC::lock(); }
GCLock::~GCLock(void) { GC::unlock(); }
class FreeListNode : public ASTNode {
public:
FreeListNode* next;
size_t size;
FreeListNode(size_t s, FreeListNode* n) : ASTNode(ASTNode::NID_FL), next(n), size(s) {
_gc_mark = 1;
}
FreeListNode(size_t s) : ASTNode(ASTNode::NID_FL), next(NULL), size(s) {}
};
class HeapPage {
public:
HeapPage* next;
size_t size;
size_t used;
char data[1];
HeapPage(HeapPage* n, size_t s) : next(n), size(s), used(0) {}
};
/// Memory managed by the garbage collector
class GC::Heap {
friend class GC;
#if defined(MINIZINC_GC_STATS)
static const char* _nodeid[Item::II_END + 1];
struct GCStat {
int first;
int second;
int keepalive;
int inmodel;
size_t total;
GCStat(void) : first(0), second(0), keepalive(0), inmodel(0), total(0) {}
};
std::map<int, GCStat> gc_stats;
#endif
protected:
static const size_t _min_gc_threshold;
HeapPage* _page;
Model* _rootset;
KeepAlive* _roots;
WeakRef* _weakRefs;
ASTNodeWeakMap* _nodeWeakMaps;
static const int _max_fl = 5;
FreeListNode* _fl[_max_fl + 1];
static const size_t _fl_size[_max_fl + 1];
int _fl_slot(size_t _size) {
size_t size = _size;
assert(size <= _fl_size[_max_fl]);
assert(size >= _fl_size[0]);
size -= sizeof(Item);
assert(size % sizeof(void*) == 0);
size /= sizeof(void*);
assert(size >= 1);
int slot = static_cast<int>(size) - 1;
return slot;
}
/// Total amount of memory allocated
size_t _alloced_mem;
/// Total amount of memory currently free
size_t _free_mem;
/// Memory threshold for next garbage collection
size_t _gc_threshold;
/// High water mark of all allocated memory
size_t _max_alloced_mem;
/// A trail item
struct TItem {
Expression** l;
Expression* v;
bool mark;
TItem(Expression** l0, Expression* v0) : l(l0), v(v0), mark(false) {}
};
/// Trail
std::vector<TItem> trail;
Heap(void)
: _page(NULL),
_rootset(NULL),
_roots(NULL),
_weakRefs(NULL),
_nodeWeakMaps(NULL),
_alloced_mem(0),
_free_mem(0),
_gc_threshold(_min_gc_threshold),
_max_alloced_mem(0) {
for (int i = _max_fl + 1; i--;) _fl[i] = NULL;
}
/// Default size of pages to allocate
static const size_t pageSize = 1 << 20;
HeapPage* allocPage(size_t s, bool exact = false) {
if (!exact) s = std::max(s, pageSize);
HeapPage* newPage = static_cast<HeapPage*>(::malloc(sizeof(HeapPage) + s - 1));
if (newPage == NULL) {
throw InternalError("out of memory");
}
#ifndef NDEBUG
memset(newPage, 255, sizeof(HeapPage) + s - 1);
#endif
_alloced_mem += s;
_max_alloced_mem = std::max(_max_alloced_mem, _alloced_mem);
_free_mem += s;
if (exact && _page) {
new (newPage) HeapPage(_page->next, s);
_page->next = newPage;
} else {
if (_page) {
size_t ns = _page->size - _page->used;
assert(ns <= _fl_size[_max_fl]);
if (ns >= _fl_size[0]) {
// Remainder of page can be added to free lists
FreeListNode* fln = reinterpret_cast<FreeListNode*>(_page->data + _page->used);
_page->used += ns;
new (fln) FreeListNode(ns, _fl[_fl_slot(ns)]);
_fl[_fl_slot(ns)] = fln;
} else {
// Waste a little memory (less than smallest free list slot)
_free_mem -= ns;
assert(_alloced_mem >= _free_mem);
}
}
new (newPage) HeapPage(_page, s);
_page = newPage;
}
return newPage;
}
void* alloc(size_t size, bool exact = false) {
assert(size <= 80 || exact);
/// Align to word boundary
size += ((8 - (size & 7)) & 7);
HeapPage* p = _page;
if (exact || _page == NULL || _page->used + size >= _page->size) p = allocPage(size, exact);
char* ret = p->data + p->used;
p->used += size;
_free_mem -= size;
if (p->size - p->used < _fl_size[0]) {
_free_mem -= (p->size - p->used);
_alloced_mem -= (p->size - p->used);
p->size = p->used;
}
assert(_alloced_mem >= _free_mem);
return ret;
}
/// Allocate one object of type T (no initialisation)
template <typename T>
T* alloc(void) {
return static_cast<T*>(alloc(sizeof(T)));
}
/// Allocate \a n objects of type T (no initialisation)
template <typename T>
T* alloc(int n) {
return static_cast<T*>(alloc(n * sizeof(T)));
}
void* fl(size_t size) {
int slot = _fl_slot(size);
assert(slot <= _max_fl);
if (_fl[slot]) {
FreeListNode* p = _fl[slot];
_fl[slot] = p->next;
_free_mem -= size;
return p;
}
return alloc(size);
}
void trigger(void) {
DTRACE0(GC_START);
#ifdef MINIZINC_GC_STATS
std::cerr << "GC\n\talloced " << (_alloced_mem / 1024) << "\n\tfree " << (_free_mem / 1024)
<< "\n\tdiff " << ((_alloced_mem - _free_mem) / 1024) << "\n\tthreshold "
<< (_gc_threshold / 1024) << "\n";
#endif
size_t old_free = _free_mem;
mark();
sweep();
// GC strategy:
// increase threshold if either
// a) we haven't been able to put much on the free list (comapred to before GC), or
// b) the free list memory (after GC) is less than 50% of the allocated memory
// otherwise (i.e., we have been able to increase the free list, and it is now more
// than 50% of overall allocated memory), keep threshold at allocated memory
if ((old_free != 0 && static_cast<double>(old_free) / static_cast<double>(_free_mem) > 0.9) ||
static_cast<double>(_free_mem) / _alloced_mem < 0.5) {
_gc_threshold = std::max(_min_gc_threshold, static_cast<size_t>(_alloced_mem * 1.5));
} else {
_gc_threshold = std::max(_min_gc_threshold, _alloced_mem);
}
#ifdef MINIZINC_GC_STATS
std::cerr << "done\n\talloced " << (_alloced_mem / 1024) << "\n\tfree " << (_free_mem / 1024)
<< "\n\tdiff " << ((_alloced_mem - _free_mem) / 1024) << "\n\tthreshold "
<< (_gc_threshold / 1024) << "\n";
#endif
DTRACE0(GC_END);
}
void rungc(void) {
if (_alloced_mem > _gc_threshold) {
trigger();
}
}
void mark(void);
void sweep(void);
static size_t nodesize(ASTNode* n) {
static const size_t _nodesize[Item::II_END + 1] = {
0, // NID_FL
0, // NID_CHUNK
0, // NID_VEC
sizeof(IntLit), // E_INTLIT
sizeof(FloatLit), // E_FLOATLIT
sizeof(SetLit), // E_SETLIT
sizeof(BoolLit), // E_BOOLLIT
sizeof(StringLit), // E_STRINGLIT
sizeof(Id), // E_ID
sizeof(AnonVar), // E_ANON
sizeof(ArrayLit), // E_ARRAYLIT
sizeof(ArrayAccess), // E_ARRAYACCESS
sizeof(Comprehension), // E_COMP
sizeof(ITE), // E_ITE
sizeof(BinOp), // E_BINOP
sizeof(UnOp), // E_UNOP
sizeof(Call), // E_CALL
sizeof(VarDecl), // E_VARDECL
sizeof(Let), // E_LET
sizeof(TypeInst), // E_TI
sizeof(TIId), // E_TIID
sizeof(IncludeI), // II_INC
sizeof(VarDeclI), // II_VD
sizeof(AssignI), // II_ASN
sizeof(ConstraintI), // II_CON
sizeof(SolveI), // II_SOL
sizeof(OutputI), // II_OUT
sizeof(FunctionI) // II_FUN
};
size_t ns;
switch (n->_id) {
case ASTNode::NID_FL:
ns = static_cast<FreeListNode*>(n)->size;
break;
case ASTNode::NID_CHUNK:
ns = static_cast<ASTChunk*>(n)->memsize();
break;
case ASTNode::NID_VEC:
ns = static_cast<ASTVec*>(n)->memsize();
break;
default:
assert(n->_id <= Item::II_END);
ns = _nodesize[n->_id];
break;
}
ns += ((8 - (ns & 7)) & 7);
return ns;
}
};
const size_t GC::Heap::_min_gc_threshold = 10ll * 1024ll;
#ifdef MINIZINC_GC_STATS
const char* GC::Heap::_nodeid[] = {
"FreeList ", // NID_FL
"Chunk ", // NID_CHUNK
"Vec ", // NID_VEC
"IntLit ", // E_INTLIT
"FloatLit ", // E_FLOATLIT
"SetLit ", // E_SETLIT
"BoolLit ", // E_BOOLLIT
"StringLit ", // E_STRINGLIT
"Id ", // E_ID
"AnonVar ", // E_ANON
"ArrayLit ", // E_ARRAYLIT
"ArrayAccess ", // E_ARRAYACCESS
"Comprehension ", // E_COMP
"ITE ", // E_ITE
"BinOp ", // E_BINOP
"UnOp ", // E_UNOP
"Call ", // E_CALL
"VarDecl ", // E_VARDECL
"Let ", // E_LET
"TypeInst ", // E_TI
"TIId ", // E_TIID
"IncludeI ", // II_INC
"VarDeclI ", // II_VD
"AssignI ", // II_ASN
"ConstraintI ", // II_CON
"SolveI ", // II_SOL
"OutputI ", // II_OUT
"FunctionI " // II_FUN
};
#endif
void GC::setTimeout(unsigned long long int t) {
if (gc() == NULL) {
gc() = new GC();
}
gc()->_timeout = t;
gc()->_timeout_timer.reset();
}
void GC::lock(void) {
if (gc() == NULL) {
gc() = new GC();
}
// If a timeout has been specified, first check counter
// before checking timer (counter is much cheaper, introduces
// less overhead)
if (gc()->_timeout > 0 && gc()->_timeout_counter++ > 500) {
gc()->_timeout_counter = 0;
if (gc()->_timeout_timer.ms() > gc()->_timeout) {
gc()->_timeout = 0;
gc()->_timeout_counter = 0;
throw Timeout();
}
}
if (gc()->_lock_count == 0) {
gc()->_heap->rungc();
}
gc()->_lock_count++;
}
void GC::unlock(void) {
assert(locked());
gc()->_lock_count--;
}
void GC::trigger(void) {
if (!locked()) gc()->_heap->trigger();
}
const size_t GC::Heap::pageSize;
const size_t GC::Heap::_fl_size[GC::Heap::_max_fl + 1] = {
sizeof(Item) + 1 * sizeof(void*), sizeof(Item) + 2 * sizeof(void*),
sizeof(Item) + 3 * sizeof(void*), sizeof(Item) + 4 * sizeof(void*),
sizeof(Item) + 5 * sizeof(void*), sizeof(Item) + 6 * sizeof(void*),
};
GC::GC(void) : _heap(new Heap()), _lock_count(0), _timeout(0), _timeout_counter(0) {}
void GC::add(Model* m) {
GC* gc = GC::gc();
if (gc->_heap->_rootset) {
m->_roots_next = gc->_heap->_rootset;
m->_roots_prev = m->_roots_next->_roots_prev;
m->_roots_prev->_roots_next = m;
m->_roots_next->_roots_prev = m;
} else {
gc->_heap->_rootset = m->_roots_next = m->_roots_prev = m;
}
}
void GC::remove(Model* m) {
GC* gc = GC::gc();
if (m->_roots_next == m) {
gc->_heap->_rootset = NULL;
} else {
m->_roots_next->_roots_prev = m->_roots_prev;
m->_roots_prev->_roots_next = m->_roots_next;
if (m == gc->_heap->_rootset) gc->_heap->_rootset = m->_roots_prev;
}
}
void* GC::alloc(size_t size) {
assert(locked());
void* ret;
if (size < _heap->_fl_size[0] || size > _heap->_fl_size[_heap->_max_fl]) {
ret = _heap->alloc(size, true);
} else {
ret = _heap->fl(size);
}
new (ret) FreeListNode(size);
return ret;
}
void GC::Heap::mark(void) {
#if defined(MINIZINC_GC_STATS)
std::cerr << "================= mark =================: ";
gc_stats.clear();
#endif
for (KeepAlive* e = _roots; e != NULL; e = e->next()) {
if ((*e)() && (*e)()->_gc_mark == 0) {
Expression::mark((*e)());
#if defined(MINIZINC_GC_STATS)
gc_stats[(*e)()->_id].keepalive++;
#endif
}
}
#if defined(MINIZINC_GC_STATS)
std::cerr << "+";
#endif
Model* m = _rootset;
if (m == NULL) return;
do {
m->_filepath.mark();
m->_filename.mark();
for (unsigned int j = 0; j < m->_items.size(); j++) {
Item* i = m->_items[j];
if (i->_gc_mark == 0) {
i->_gc_mark = 1;
i->loc().mark();
switch (i->iid()) {
case Item::II_INC:
i->cast<IncludeI>()->f().mark();
break;
case Item::II_VD:
Expression::mark(i->cast<VarDeclI>()->e());
#if defined(MINIZINC_GC_STATS)
gc_stats[i->cast<VarDeclI>()->e()->Expression::eid()].inmodel++;
#endif
break;
case Item::II_ASN:
i->cast<AssignI>()->id().mark();
Expression::mark(i->cast<AssignI>()->e());
Expression::mark(i->cast<AssignI>()->decl());
break;
case Item::II_CON:
Expression::mark(i->cast<ConstraintI>()->e());
#if defined(MINIZINC_GC_STATS)
gc_stats[i->cast<ConstraintI>()->e()->Expression::eid()].inmodel++;
#endif
break;
case Item::II_SOL: {
SolveI* si = i->cast<SolveI>();
for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) {
Expression::mark(*it);
}
}
Expression::mark(i->cast<SolveI>()->e());
break;
case Item::II_OUT:
Expression::mark(i->cast<OutputI>()->e());
break;
case Item::II_FUN: {
FunctionI* fi = i->cast<FunctionI>();
fi->id().mark();
Expression::mark(fi->ti());
for (ExpressionSetIter it = fi->ann().begin(); it != fi->ann().end(); ++it) {
Expression::mark(*it);
}
Expression::mark(fi->e());
fi->params().mark();
for (unsigned int k = 0; k < fi->params().size(); k++) {
Expression::mark(fi->params()[k]);
}
} break;
}
}
}
m = m->_roots_next;
} while (m != _rootset);
for (unsigned int i = static_cast<unsigned int>(trail.size()); i--;) {
Expression::mark(trail[i].v);
}
bool fixPrev = false;
WeakRef* prevWr = NULL;
for (WeakRef* wr = _weakRefs; wr != NULL; wr = wr->next()) {
if (fixPrev) {
fixPrev = false;
removeWeakRef(prevWr);
prevWr->_n = NULL;
prevWr->_p = NULL;
}
if ((*wr)() && (*wr)()->_gc_mark == 0) {
wr->_e = NULL;
wr->_valid = false;
fixPrev = true;
prevWr = wr;
}
}
if (fixPrev) {
removeWeakRef(prevWr);
prevWr->_n = NULL;
prevWr->_p = NULL;
}
for (ASTNodeWeakMap* wr = _nodeWeakMaps; wr != NULL; wr = wr->next()) {
std::vector<ASTNode*> toRemove;
for (auto n : wr->_m) {
if (n.first->_gc_mark == 0 || n.second->_gc_mark == 0) toRemove.push_back(n.first);
}
for (auto n : toRemove) {
wr->_m.erase(n);
}
}
#if defined(MINIZINC_GC_STATS)
std::cerr << "+";
std::cerr << "\n";
#endif
}
void GC::Heap::sweep(void) {
#if defined(MINIZINC_GC_STATS)
std::cerr << "=============== GC sweep =============\n";
#endif
HeapPage* p = _page;
HeapPage* prev = NULL;
while (p) {
size_t off = 0;
bool wholepage = true;
struct NodeInfo {
ASTNode* n;
size_t ns;
NodeInfo(ASTNode* n0, size_t ns0) : n(n0), ns(ns0) {}
};
std::vector<NodeInfo> freeNodes;
freeNodes.reserve(100);
while (off < p->used) {
ASTNode* n = reinterpret_cast<ASTNode*>(p->data + off);
size_t ns = nodesize(n);
assert(ns != 0);
#if defined(MINIZINC_GC_STATS)
GCStat& stats = gc_stats[n->_id];
stats.first++;
stats.total += ns;
#endif
if (n->_gc_mark == 0) {
switch (n->_id) {
case Item::II_FUN:
static_cast<FunctionI*>(n)->ann().~Annotation();
break;
case Item::II_SOL:
static_cast<SolveI*>(n)->ann().~Annotation();
break;
case Expression::E_VARDECL:
// Reset WeakRef inside VarDecl
static_cast<VarDecl*>(n)->flat(NULL);
// fall through
default:
if (n->_id >= ASTNode::NID_END + 1 && n->_id <= Expression::EID_END) {
static_cast<Expression*>(n)->ann().~Annotation();
}
}
if (ns >= _fl_size[0] && ns <= _fl_size[_max_fl]) {
freeNodes.push_back(NodeInfo(n, ns));
} else {
assert(off == 0);
assert(p->used == p->size);
}
} else {
#if defined(MINIZINC_GC_STATS)
stats.second++;
#endif
wholepage = false;
if (n->_id != ASTNode::NID_FL) n->_gc_mark = 0;
}
off += ns;
}
if (wholepage) {
#ifndef NDEBUG
memset(p->data, 42, p->size);
#endif
if (prev) {
prev->next = p->next;
} else {
assert(p == _page);
_page = p->next;
}
HeapPage* pf = p;
p = p->next;
_alloced_mem -= pf->size;
if (pf->size - pf->used >= _fl_size[0]) _free_mem -= (pf->size - pf->used);
assert(_alloced_mem >= _free_mem);
::free(pf);
} else {
for (auto ni : freeNodes) {
FreeListNode* fln = static_cast<FreeListNode*>(ni.n);
new (fln) FreeListNode(ni.ns, _fl[_fl_slot(ni.ns)]);
_fl[_fl_slot(ni.ns)] = fln;
_free_mem += ni.ns;
#if defined(MINIZINC_GC_STATS)
gc_stats[fln->_id].second++;
#endif
}
assert(_alloced_mem >= _free_mem);
prev = p;
p = p->next;
}
}
#if defined(MINIZINC_GC_STATS)
for (auto stat : gc_stats) {
std::cerr << _nodeid[stat.first] << ":\t" << stat.second.first << " / " << stat.second.second
<< " / " << stat.second.keepalive << " / " << stat.second.inmodel << " / ";
if (stat.second.total > 1024) {
if (stat.second.total > 1024 * 1024) {
std::cerr << (stat.second.total / 1024 / 1024) << "M";
} else {
std::cerr << (stat.second.total / 1024) << "K";
}
} else {
std::cerr << (stat.second.total);
}
std::cerr << std::endl;
}
#endif
}
ASTVec::ASTVec(size_t size) : ASTNode(NID_VEC), _size(size) {}
void* ASTVec::alloc(size_t size) {
size_t s = sizeof(ASTVec) + (size <= 2 ? 0 : size - 2) * sizeof(void*);
s += ((8 - (s & 7)) & 7);
return GC::gc()->alloc(s);
}
ASTChunk::ASTChunk(size_t size) : ASTNode(NID_CHUNK), _size(size) {}
void* ASTChunk::alloc(size_t size) {
size_t s = sizeof(ASTChunk) + (size <= 4 ? 0 : size - 4) * sizeof(char);
s += ((8 - (s & 7)) & 7);
return GC::gc()->alloc(s);
}
void GC::mark(void) {
GC* gc = GC::gc();
if (!gc->_heap->trail.empty()) gc->_heap->trail.back().mark = true;
}
void GC::trail(Expression** l, Expression* v) {
GC* gc = GC::gc();
gc->_heap->trail.push_back(GC::Heap::TItem(l, v));
}
void GC::untrail(void) {
GC* gc = GC::gc();
while (!gc->_heap->trail.empty() && !gc->_heap->trail.back().mark) {
*gc->_heap->trail.back().l = gc->_heap->trail.back().v;
gc->_heap->trail.pop_back();
}
if (!gc->_heap->trail.empty()) gc->_heap->trail.back().mark = false;
}
size_t GC::maxMem(void) {
GC* gc = GC::gc();
return gc->_heap->_max_alloced_mem;
}
void* ASTNode::operator new(size_t size) { return GC::gc()->alloc(size); }
void GC::addKeepAlive(KeepAlive* e) {
assert(e->_p == NULL);
assert(e->_n == NULL);
e->_n = GC::gc()->_heap->_roots;
if (GC::gc()->_heap->_roots) GC::gc()->_heap->_roots->_p = e;
GC::gc()->_heap->_roots = e;
}
void GC::removeKeepAlive(KeepAlive* e) {
if (e->_p) {
e->_p->_n = e->_n;
} else {
assert(GC::gc()->_heap->_roots == e);
GC::gc()->_heap->_roots = e->_n;
}
if (e->_n) {
e->_n->_p = e->_p;
}
}
KeepAlive::KeepAlive(Expression* e) : _e(e), _p(NULL), _n(NULL) {
if (_e && !_e->isUnboxedVal()) GC::gc()->addKeepAlive(this);
}
KeepAlive::~KeepAlive(void) {
if (_e && !_e->isUnboxedVal()) GC::gc()->removeKeepAlive(this);
}
KeepAlive::KeepAlive(const KeepAlive& e) : _e(e._e), _p(NULL), _n(NULL) {
if (_e && !_e->isUnboxedVal()) GC::gc()->addKeepAlive(this);
}
KeepAlive& KeepAlive::operator=(const KeepAlive& e) {
if (_e && !_e->isUnboxedVal()) {
if (e._e == NULL || e._e->isUnboxedVal()) {
GC::gc()->removeKeepAlive(this);
_p = _n = NULL;
}
} else {
if (e._e != NULL && !e._e->isUnboxedVal()) GC::gc()->addKeepAlive(this);
}
_e = e._e;
return *this;
}
void GC::addWeakRef(WeakRef* e) {
assert(e->_p == NULL);
assert(e->_n == NULL);
e->_n = GC::gc()->_heap->_weakRefs;
if (GC::gc()->_heap->_weakRefs) GC::gc()->_heap->_weakRefs->_p = e;
GC::gc()->_heap->_weakRefs = e;
}
void GC::removeWeakRef(MiniZinc::WeakRef* e) {
if (e->_p) {
e->_p->_n = e->_n;
} else {
assert(GC::gc()->_heap->_weakRefs == e);
GC::gc()->_heap->_weakRefs = e->_n;
}
if (e->_n) {
e->_n->_p = e->_p;
}
}
void GC::addNodeWeakMap(ASTNodeWeakMap* m) {
assert(m->_p == NULL);
assert(m->_n == NULL);
m->_n = GC::gc()->_heap->_nodeWeakMaps;
if (GC::gc()->_heap->_nodeWeakMaps) GC::gc()->_heap->_nodeWeakMaps->_p = m;
GC::gc()->_heap->_nodeWeakMaps = m;
}
void GC::removeNodeWeakMap(ASTNodeWeakMap* m) {
if (m->_p) {
m->_p->_n = m->_n;
} else {
assert(GC::gc()->_heap->_nodeWeakMaps == m);
GC::gc()->_heap->_nodeWeakMaps = m->_n;
}
if (m->_n) {
m->_n->_p = m->_p;
}
}
WeakRef::WeakRef(Expression* e) : _e(e), _p(NULL), _n(NULL), _valid(true) {
if (_e && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this);
}
WeakRef::~WeakRef(void) {
if (_e && !_e->isUnboxedVal()) GC::gc()->removeWeakRef(this);
}
WeakRef::WeakRef(const WeakRef& e) : _e(e()), _p(NULL), _n(NULL), _valid(true) {
if (_e && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this);
}
WeakRef& WeakRef::operator=(const WeakRef& e) {
// Test if this WeakRef is currently active in the GC
bool isActive = (_e && !_e->isUnboxedVal());
if (isActive) {
// Yes, active WeakRef.
// If after assigning WeakRef should be inactive, remove it.
if (e() == NULL || e()->isUnboxedVal()) {
GC::gc()->removeWeakRef(this);
_n = _p = NULL;
}
}
_e = e();
_valid = true;
// If this WeakRef was not active but now should be, add it
if (!isActive && _e != NULL && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this);
return *this;
}
ASTNodeWeakMap::ASTNodeWeakMap(void) : _p(NULL), _n(NULL) { GC::gc()->addNodeWeakMap(this); }
ASTNodeWeakMap::~ASTNodeWeakMap(void) { GC::gc()->removeNodeWeakMap(this); }
void ASTNodeWeakMap::insert(ASTNode* n0, ASTNode* n1) { _m.insert(std::make_pair(n0, n1)); }
ASTNode* ASTNodeWeakMap::find(ASTNode* n) {
NodeMap::iterator it = _m.find(n);
if (it == _m.end()) return NULL;
return it->second;
}
} // namespace MiniZinc