00001 #include "nasal.h"
00002 #include "data.h"
00003 #include "code.h"
00004
00005 #define MIN_BLOCK_SIZE 32
00006
00007 static void reap(struct naPool* p);
00008 static void mark(naRef r);
00009
00010 struct Block {
00011 int size;
00012 char* block;
00013 struct Block* next;
00014 };
00015
00016
00017 static void freeDead()
00018 {
00019 int i;
00020 for(i=0; i<globals->ndead; i++)
00021 naFree(globals->deadBlocks[i]);
00022 globals->ndead = 0;
00023 }
00024
00025 static void marktemps(struct Context* c)
00026 {
00027 int i;
00028 naRef r = naNil();
00029 for(i=0; i<c->ntemps; i++) {
00030 SETPTR(r, c->temps[i]);
00031 mark(r);
00032 }
00033 }
00034
00035
00036 static void garbageCollect()
00037 {
00038 int i;
00039 struct Context* c;
00040 globals->allocCount = 0;
00041 c = globals->allContexts;
00042 while(c) {
00043 for(i=0; i<NUM_NASAL_TYPES; i++)
00044 c->nfree[i] = 0;
00045 for(i=0; i < c->fTop; i++) {
00046 mark(c->fStack[i].func);
00047 mark(c->fStack[i].locals);
00048 }
00049 for(i=0; i < c->opTop; i++)
00050 mark(c->opStack[i]);
00051 mark(c->dieArg);
00052 marktemps(c);
00053 c = c->nextAll;
00054 }
00055
00056 mark(globals->save);
00057 mark(globals->symbols);
00058 mark(globals->meRef);
00059 mark(globals->argRef);
00060 mark(globals->parentsRef);
00061
00062
00063 for(i=0; i<NUM_NASAL_TYPES; i++)
00064 reap(&(globals->pools[i]));
00065
00066
00067
00068
00069
00070 if(globals->deadsz < globals->allocCount) {
00071 globals->deadsz = globals->allocCount;
00072 if(globals->deadsz < 256) globals->deadsz = 256;
00073 naFree(globals->deadBlocks);
00074 globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
00075 }
00076 globals->needGC = 0;
00077 }
00078
00079 void naModLock()
00080 {
00081 LOCK();
00082 globals->nThreads++;
00083 UNLOCK();
00084 naCheckBottleneck();
00085 }
00086
00087 void naModUnlock()
00088 {
00089 LOCK();
00090 globals->nThreads--;
00091
00092
00093
00094 if(globals->waitCount == globals->nThreads)
00095 naSemUp(globals->sem, 1);
00096 UNLOCK();
00097 }
00098
00099
00100
00101
00102
00103
00104 static void bottleneck()
00105 {
00106 struct Globals* g = globals;
00107 g->bottleneck = 1;
00108 while(g->bottleneck && g->waitCount < g->nThreads - 1) {
00109 g->waitCount++;
00110 UNLOCK(); naSemDown(g->sem); LOCK();
00111 g->waitCount--;
00112 }
00113 if(g->waitCount >= g->nThreads - 1) {
00114 freeDead();
00115 if(g->needGC) garbageCollect();
00116 if(g->waitCount) naSemUp(g->sem, g->waitCount);
00117 g->bottleneck = 0;
00118 }
00119 }
00120
00121 void naCheckBottleneck()
00122 {
00123 if(globals->bottleneck) { LOCK(); bottleneck(); UNLOCK(); }
00124 }
00125
00126 static void naCode_gcclean(struct naCode* o)
00127 {
00128 naFree(o->constants); o->constants = 0;
00129 }
00130
00131 static void naGhost_gcclean(struct naGhost* g)
00132 {
00133 if(g->ptr && g->gtype->destroy) g->gtype->destroy(g->ptr);
00134 g->ptr = 0;
00135 }
00136
00137 static void freeelem(struct naPool* p, struct naObj* o)
00138 {
00139
00140 switch(p->type) {
00141 case T_STR: naStr_gcclean ((struct naStr*) o); break;
00142 case T_VEC: naVec_gcclean ((struct naVec*) o); break;
00143 case T_HASH: naiGCHashClean ((struct naHash*) o); break;
00144 case T_CODE: naCode_gcclean ((struct naCode*) o); break;
00145 case T_GHOST: naGhost_gcclean((struct naGhost*)o); break;
00146 }
00147 p->free[p->nfree++] = o;
00148 }
00149
00150 static void newBlock(struct naPool* p, int need)
00151 {
00152 int i;
00153 struct Block* newb;
00154
00155 if(need < MIN_BLOCK_SIZE) need = MIN_BLOCK_SIZE;
00156
00157 newb = naAlloc(sizeof(struct Block));
00158 newb->block = naAlloc(need * p->elemsz);
00159 newb->size = need;
00160 newb->next = p->blocks;
00161 p->blocks = newb;
00162 naBZero(newb->block, need * p->elemsz);
00163
00164 if(need > p->freesz - p->freetop) need = p->freesz - p->freetop;
00165 p->nfree = 0;
00166 p->free = p->free0 + p->freetop;
00167 for(i=0; i < need; i++) {
00168 struct naObj* o = (struct naObj*)(newb->block + i*p->elemsz);
00169 o->mark = 0;
00170 p->free[p->nfree++] = o;
00171 }
00172 p->freetop += need;
00173 }
00174
00175 void naGC_init(struct naPool* p, int type)
00176 {
00177 p->type = type;
00178 p->elemsz = naTypeSize(type);
00179 p->blocks = 0;
00180
00181 p->free0 = p->free = 0;
00182 p->nfree = p->freesz = p->freetop = 0;
00183 reap(p);
00184 }
00185
00186 static int poolsize(struct naPool* p)
00187 {
00188 int total = 0;
00189 struct Block* b = p->blocks;
00190 while(b) { total += b->size; b = b->next; }
00191 return total;
00192 }
00193
00194 struct naObj** naGC_get(struct naPool* p, int n, int* nout)
00195 {
00196 struct naObj** result;
00197 naCheckBottleneck();
00198 LOCK();
00199 while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) {
00200 globals->needGC = 1;
00201 bottleneck();
00202 }
00203 if(p->nfree == 0)
00204 newBlock(p, poolsize(p)/8);
00205 n = p->nfree < n ? p->nfree : n;
00206 *nout = n;
00207 p->nfree -= n;
00208 globals->allocCount -= n;
00209 result = (struct naObj**)(p->free + p->nfree);
00210 UNLOCK();
00211 return result;
00212 }
00213
00214 static void markvec(naRef r)
00215 {
00216 int i;
00217 struct VecRec* vr = PTR(r).vec->rec;
00218 if(!vr) return;
00219 for(i=0; i<vr->size; i++)
00220 mark(vr->array[i]);
00221 }
00222
00223
00224
00225 static void mark(naRef r)
00226 {
00227 int i;
00228
00229 if(IS_NUM(r) || IS_NIL(r))
00230 return;
00231
00232 if(PTR(r).obj->mark == 1)
00233 return;
00234
00235 PTR(r).obj->mark = 1;
00236 switch(PTR(r).obj->type) {
00237 case T_VEC: markvec(r); break;
00238 case T_HASH: naiGCMarkHash(r); break;
00239 case T_CODE:
00240 mark(PTR(r).code->srcFile);
00241 for(i=0; i<PTR(r).code->nConstants; i++)
00242 mark(PTR(r).code->constants[i]);
00243 break;
00244 case T_FUNC:
00245 mark(PTR(r).func->code);
00246 mark(PTR(r).func->namespace);
00247 mark(PTR(r).func->next);
00248 break;
00249 }
00250 }
00251
00252 void naiGCMark(naRef r)
00253 {
00254 mark(r);
00255 }
00256
00257
00258
00259 static void reap(struct naPool* p)
00260 {
00261 struct Block* b;
00262 int elem, freesz, total = poolsize(p);
00263 freesz = total < MIN_BLOCK_SIZE ? MIN_BLOCK_SIZE : total;
00264 freesz = (3 * freesz / 2) + (globals->nThreads * OBJ_CACHE_SZ);
00265 if(p->freesz < freesz) {
00266 naFree(p->free0);
00267 p->freesz = freesz;
00268 p->free = p->free0 = naAlloc(sizeof(void*) * p->freesz);
00269 }
00270
00271 p->nfree = 0;
00272 p->free = p->free0;
00273
00274 for(b = p->blocks; b; b = b->next)
00275 for(elem=0; elem < b->size; elem++) {
00276 struct naObj* o = (struct naObj*)(b->block + elem * p->elemsz);
00277 if(o->mark == 0)
00278 freeelem(p, o);
00279 o->mark = 0;
00280 }
00281
00282 p->freetop = p->nfree;
00283
00284
00285 globals->allocCount += total/2;
00286
00287
00288
00289 if(p->nfree < total/4) {
00290 int used = total - p->nfree;
00291 int avail = total - used;
00292 int need = used/2 - avail;
00293 if(need > 0)
00294 newBlock(p, need);
00295 }
00296 }
00297
00298
00299 static void* doswap(void** target, void* val)
00300 {
00301 void* old = *target;
00302 *target = val;
00303 return old;
00304 }
00305
00306
00307
00308
00309 void naGC_swapfree(void** target, void* val)
00310 {
00311 void* old;
00312 LOCK();
00313 old = doswap(target, val);
00314 while(globals->ndead >= globals->deadsz)
00315 bottleneck();
00316 globals->deadBlocks[globals->ndead++] = old;
00317 UNLOCK();
00318 }