blob: 4b166cbef8f63b4d6a52eaf1f0b9ed33d8772fb0 [file] [log] [blame]
//===----------- gccollector.cc - Mvm Garbage Collector -------------------===//
//
// Mvm
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "gccollector.h"
#include <setjmp.h>
using namespace mvm;
GCAllocator *GCCollector::allocator = 0;
#ifdef HAVE_PTHREAD
GCThread *GCCollector::threads;
#endif
gc::markerFn GCCollector::_marker;
int GCCollector::_collect_freq_auto;
int GCCollector::_collect_freq_maybe;
int GCCollector::_since_last_collection;
bool GCCollector::_enable_auto;
bool GCCollector::_enable_maybe;
bool GCCollector::_enable_collection;
int GCCollector::status;
GCChunkNode *GCCollector::used_nodes;
GCChunkNode *GCCollector::unused_nodes;
unsigned int GCCollector::current_mark;
#ifdef HAVE_PTHREAD
void GCCollector::siggc_handler(int) {
GCThreadCollector *loc = threads->myloc();
register unsigned int cm = current_mark;
// jmp_buf buf;
// setjmp(buf);
threads->stackLock();
if(!loc) /* arrive lorsqu'on est en train de détruire une clé */
threads->another_mark();
else if(loc->current_mark() != cm) {
register unsigned int **cur = (unsigned int **)&cur;
register unsigned int **max = loc->base_sp();
GCChunkNode *node;
for(; cur<max; cur++) {
if((node = o2node(*cur)) && (!isMarked(node))) {
node->remove();
node->append(used_nodes);
mark(node);
}
}
loc->current_mark(cm);
threads->another_mark();
threads->waitCollection();
}
threads->stackUnlock();
}
#endif
void GCCollector::do_collect() {
//printf("----- do collect -----\n");
jmp_buf buf;
setjmp(buf);
GCChunkNode *cur;
_since_last_collection = _collect_freq_auto;
current_mark++;
unused_nodes->attrape(used_nodes);
#ifdef HAVE_PTHREAD
threads->synchronize();
#endif
for(cur=used_nodes->next(); cur!=used_nodes; cur=cur->next())
trace(cur);
if(_marker)
_marker();
status = stat_finalize;
/* finalize */
GCChunkNode finalizable;
finalizable.attrape(unused_nodes);
status = stat_alloc;
#ifdef HAVE_PTHREAD
threads->collectionFinished();
#endif
/* on tue tout le monde */
GCChunkNode *next;
for(cur=finalizable.next(); cur!=&finalizable; cur=next) {
register gc_header *c = cur->chunk();
next = cur->next();
c->_2gc()->destroyer(real_nbb(cur));
//printf(" !!!! reject %p [%p]\n", cur->chunk()->_2gc(), cur);
allocator->reject_chunk(cur);
}
// printf(" collection finished\n");
// /* voilà, c'est fini :) */
// stacklock_fini.broadcast();
// // printf("---- End collect\n");
}
void GCCollector::collect_unprotect() {
if(_enable_collection && (status == stat_alloc)) {
status = stat_collect;
do_collect();
}
}
#ifdef HAVE_PTHREAD
void GCCollector::die_if_sigsegv_occured_during_collection(void *addr) {
if(!isStable(0, 0, 0, 0, 0, 0, 0, 0, 0)) {
printf("; ****************************************************** ;\n");
printf("; SIGSEGV occured during a collection ;\n");
printf("; I'm trying to let the allocator in a coherent stat ;\n");
printf("; but the collector is DEAD and will never collect again ;\n");
printf("; ****************************************************** ;\n");
status = stat_broken; /* indique que la collection est terminée + empeche toute nouvelle collection */
threads->cancel(); /* emule une collection complete pour débloquer (synchro) les mutateurs */
used_nodes->eat(unused_nodes); /* tous les noeuds sont utilisés... Domage pour les finalize, ils seront perdus! */
unlock_dont_recovery(); /* débloque le sémaphore du gc */
//gcfatal("SIGSEGV occured during collection at %p", addr);
}
}
#endif /* HAVE_PTHREAD */
void GCCollector::gcStats(size_t *_no, size_t *_nbb) {
register unsigned int n, tot;
register GCChunkNode *cur;
lock();
for(n=0, tot=0, cur=used_nodes->next(); cur!=used_nodes; cur=cur->next(), n++)
tot += cur->nbb();
unlock();
*_no = n;
*_nbb = tot;
}