Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ceguilua/src/lua-5.0.3/lua/lgc.c @ 1808

Last change on this file since 1808 was 1803, checked in by rgrieder, 17 years ago

added files for lua 5.1.3, lua 5.0.3, CEGUILua-0.6.1 and CEGUILua-0.5.0b

  • Property svn:eol-style set to native
File size: 13.6 KB
Line 
1/*
2** $Id: lgc.c,v 1.171b 2003/04/03 13:35:34 roberto Exp $
3** Garbage Collector
4** See Copyright Notice in lua.h
5*/
6
7#include <string.h>
8
9#define lgc_c
10
11#include "lua.h"
12
13#include "ldebug.h"
14#include "ldo.h"
15#include "lfunc.h"
16#include "lgc.h"
17#include "lmem.h"
18#include "lobject.h"
19#include "lstate.h"
20#include "lstring.h"
21#include "ltable.h"
22#include "ltm.h"
23
24
25typedef struct GCState {
26  GCObject *tmark;  /* list of marked objects to be traversed */
27  GCObject *wk;  /* list of traversed key-weak tables (to be cleared) */
28  GCObject *wv;  /* list of traversed value-weak tables */
29  GCObject *wkv;  /* list of traversed key-value weak tables */
30  global_State *g;
31} GCState;
32
33
34/*
35** some userful bit tricks
36*/
37#define setbit(x,b)     ((x) |= (1<<(b)))
38#define resetbit(x,b)   ((x) &= cast(lu_byte, ~(1<<(b))))
39#define testbit(x,b)    ((x) & (1<<(b)))
40
41#define unmark(x)       resetbit((x)->gch.marked, 0)
42#define ismarked(x)     ((x)->gch.marked & ((1<<4)|1))
43
44#define stringmark(s)   setbit((s)->tsv.marked, 0)
45
46
47#define isfinalized(u)          (!testbit((u)->uv.marked, 1))
48#define markfinalized(u)        resetbit((u)->uv.marked, 1)
49
50
51#define KEYWEAKBIT    1
52#define VALUEWEAKBIT  2
53#define KEYWEAK         (1<<KEYWEAKBIT)
54#define VALUEWEAK       (1<<VALUEWEAKBIT)
55
56
57
58#define markobject(st,o) { checkconsistency(o); \
59  if (iscollectable(o) && !ismarked(gcvalue(o))) reallymarkobject(st,gcvalue(o)); }
60
61#define condmarkobject(st,o,c) { checkconsistency(o); \
62  if (iscollectable(o) && !ismarked(gcvalue(o)) && (c)) \
63    reallymarkobject(st,gcvalue(o)); }
64
65#define markvalue(st,t) { if (!ismarked(valtogco(t))) \
66                reallymarkobject(st, valtogco(t)); }
67
68
69
70static void reallymarkobject (GCState *st, GCObject *o) {
71  lua_assert(!ismarked(o));
72  setbit(o->gch.marked, 0);  /* mark object */
73  switch (o->gch.tt) {
74    case LUA_TUSERDATA: {
75      markvalue(st, gcotou(o)->uv.metatable);
76      break;
77    }
78    case LUA_TFUNCTION: {
79      gcotocl(o)->c.gclist = st->tmark;
80      st->tmark = o;
81      break;
82    }
83    case LUA_TTABLE: {
84      gcotoh(o)->gclist = st->tmark;
85      st->tmark = o;
86      break;
87    }
88    case LUA_TTHREAD: {
89      gcototh(o)->gclist = st->tmark;
90      st->tmark = o;
91      break;
92    }
93    case LUA_TPROTO: {
94      gcotop(o)->gclist = st->tmark;
95      st->tmark = o;
96      break;
97    }
98    default: lua_assert(o->gch.tt == LUA_TSTRING);
99  }
100}
101
102
103static void marktmu (GCState *st) {
104  GCObject *u;
105  for (u = st->g->tmudata; u; u = u->gch.next) {
106    unmark(u);  /* may be marked, if left from previous GC */
107    reallymarkobject(st, u);
108  }
109}
110
111
112/* move `dead' udata that need finalization to list `tmudata' */
113size_t luaC_separateudata (lua_State *L) {
114  size_t deadmem = 0;
115  GCObject **p = &G(L)->rootudata;
116  GCObject *curr;
117  GCObject *collected = NULL;  /* to collect udata with gc event */
118  GCObject **lastcollected = &collected;
119  while ((curr = *p) != NULL) {
120    lua_assert(curr->gch.tt == LUA_TUSERDATA);
121    if (ismarked(curr) || isfinalized(gcotou(curr)))
122      p = &curr->gch.next;  /* don't bother with them */
123
124    else if (fasttm(L, gcotou(curr)->uv.metatable, TM_GC) == NULL) {
125      markfinalized(gcotou(curr));  /* don't need finalization */
126      p = &curr->gch.next;
127    }
128    else {  /* must call its gc method */
129      deadmem += sizeudata(gcotou(curr)->uv.len);
130      *p = curr->gch.next;
131      curr->gch.next = NULL;  /* link `curr' at the end of `collected' list */
132      *lastcollected = curr;
133      lastcollected = &curr->gch.next;
134    }
135  }
136  /* insert collected udata with gc event into `tmudata' list */
137  *lastcollected = G(L)->tmudata;
138  G(L)->tmudata = collected;
139  return deadmem;
140}
141
142
143static void removekey (Node *n) {
144  setnilvalue(gval(n));  /* remove corresponding value ... */
145  if (iscollectable(gkey(n)))
146    setttype(gkey(n), LUA_TNONE);  /* dead key; remove it */
147}
148
149
150static void traversetable (GCState *st, Table *h) {
151  int i;
152  int weakkey = 0;
153  int weakvalue = 0;
154  const TObject *mode;
155  markvalue(st, h->metatable);
156  lua_assert(h->lsizenode || h->node == st->g->dummynode);
157  mode = gfasttm(st->g, h->metatable, TM_MODE);
158  if (mode && ttisstring(mode)) {  /* is there a weak mode? */
159    weakkey = (strchr(svalue(mode), 'k') != NULL);
160    weakvalue = (strchr(svalue(mode), 'v') != NULL);
161    if (weakkey || weakvalue) {  /* is really weak? */
162      GCObject **weaklist;
163      h->marked &= ~(KEYWEAK | VALUEWEAK);  /* clear bits */
164      h->marked |= cast(lu_byte, (weakkey << KEYWEAKBIT) |
165                                 (weakvalue << VALUEWEAKBIT));
166      weaklist = (weakkey && weakvalue) ? &st->wkv :
167                              (weakkey) ? &st->wk :
168                                          &st->wv;
169      h->gclist = *weaklist;  /* must be cleared after GC, ... */
170      *weaklist = valtogco(h);  /* ... so put in the appropriate list */
171    }
172  }
173  if (!weakvalue) {
174    i = h->sizearray;
175    while (i--)
176      markobject(st, &h->array[i]);
177  }
178  i = sizenode(h);
179  while (i--) {
180    Node *n = gnode(h, i);
181    if (!ttisnil(gval(n))) {
182      lua_assert(!ttisnil(gkey(n)));
183      condmarkobject(st, gkey(n), !weakkey);
184      condmarkobject(st, gval(n), !weakvalue);
185    }
186  }
187}
188
189
190static void traverseproto (GCState *st, Proto *f) {
191  int i;
192  stringmark(f->source);
193  for (i=0; i<f->sizek; i++) {  /* mark literal strings */
194    if (ttisstring(f->k+i))
195      stringmark(tsvalue(f->k+i));
196  }
197  for (i=0; i<f->sizeupvalues; i++)  /* mark upvalue names */
198    stringmark(f->upvalues[i]);
199  for (i=0; i<f->sizep; i++)  /* mark nested protos */
200    markvalue(st, f->p[i]);
201  for (i=0; i<f->sizelocvars; i++)  /* mark local-variable names */
202    stringmark(f->locvars[i].varname);
203  lua_assert(luaG_checkcode(f));
204}
205
206
207
208static void traverseclosure (GCState *st, Closure *cl) {
209  if (cl->c.isC) {
210    int i;
211    for (i=0; i<cl->c.nupvalues; i++)  /* mark its upvalues */
212      markobject(st, &cl->c.upvalue[i]);
213  }
214  else {
215    int i;
216    lua_assert(cl->l.nupvalues == cl->l.p->nups);
217    markvalue(st, hvalue(&cl->l.g));
218    markvalue(st, cl->l.p);
219    for (i=0; i<cl->l.nupvalues; i++) {  /* mark its upvalues */
220      UpVal *u = cl->l.upvals[i];
221      markobject(st, u->v);
222      u->marked = 1;
223    }
224  }
225}
226
227
228static void checkstacksizes (lua_State *L, StkId max) {
229  int used = L->ci - L->base_ci;  /* number of `ci' in use */
230  if (4*used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
231    luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */
232  else condhardstacktests(luaD_reallocCI(L, L->size_ci));
233  used = max - L->stack;  /* part of stack in use */
234  if (4*used < L->stacksize && 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
235    luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */
236  else condhardstacktests(luaD_reallocstack(L, L->stacksize));
237}
238
239
240static void traversestack (GCState *st, lua_State *L1) {
241  StkId o, lim;
242  CallInfo *ci;
243  markobject(st, gt(L1));
244  lim = L1->top;
245  for (ci = L1->base_ci; ci <= L1->ci; ci++) {
246    lua_assert(ci->top <= L1->stack_last);
247    lua_assert(ci->state & (CI_C | CI_HASFRAME | CI_SAVEDPC));
248    if (lim < ci->top)
249      lim = ci->top;
250  }
251  for (o = L1->stack; o < L1->top; o++)
252    markobject(st, o);
253  for (; o <= lim; o++)
254    setnilvalue(o);
255  checkstacksizes(L1, lim);
256}
257
258
259static lu_mem propagatemarks (GCState *st) {
260  lu_mem mf = 0;
261  while (st->tmark) {  /* traverse marked objects */
262    switch (st->tmark->gch.tt) {
263      case LUA_TTABLE: {
264        Table *h = gcotoh(st->tmark);
265        st->tmark = h->gclist;
266        traversetable(st, h);
267        mf += sizeof(Table) + sizeof(TObject) * h->sizearray +
268                              sizeof(Node) * sizenode(h);
269        break;
270      }
271      case LUA_TFUNCTION: {
272        Closure *cl = gcotocl(st->tmark);
273        st->tmark = cl->c.gclist;
274        traverseclosure(st, cl);
275        mf += (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
276                            sizeLclosure(cl->l.nupvalues);
277        break;
278      }
279      case LUA_TTHREAD: {
280        lua_State *th = gcototh(st->tmark);
281        st->tmark = th->gclist;
282        traversestack(st, th);
283        mf += sizeof(lua_State) + sizeof(TObject) * th->stacksize +
284                                  sizeof(CallInfo) * th->size_ci;
285        break;
286      }
287      case LUA_TPROTO: {
288        Proto *p = gcotop(st->tmark);
289        st->tmark = p->gclist;
290        traverseproto(st, p);
291        /* do not need 'mf' for this case (cannot happen inside a udata) */
292        break;
293      }
294      default: lua_assert(0);
295    }
296  }
297  return mf;
298}
299
300
301static int valismarked (const TObject *o) {
302  if (ttisstring(o))
303    stringmark(tsvalue(o));  /* strings are `values', so are never weak */
304  return !iscollectable(o) || testbit(o->value.gc->gch.marked, 0);
305}
306
307
308/*
309** clear collected keys from weaktables
310*/
311static void cleartablekeys (GCObject *l) {
312  while (l) {
313    Table *h = gcotoh(l);
314    int i = sizenode(h);
315    lua_assert(h->marked & KEYWEAK);
316    while (i--) {
317      Node *n = gnode(h, i);
318      if (!valismarked(gkey(n)))  /* key was collected? */
319        removekey(n);  /* remove entry from table */
320    }
321    l = h->gclist;
322  }
323}
324
325
326/*
327** clear collected values from weaktables
328*/
329static void cleartablevalues (GCObject *l) {
330  while (l) {
331    Table *h = gcotoh(l);
332    int i = h->sizearray;
333    lua_assert(h->marked & VALUEWEAK);
334    while (i--) {
335      TObject *o = &h->array[i];
336      if (!valismarked(o))  /* value was collected? */
337        setnilvalue(o);  /* remove value */
338    }
339    i = sizenode(h);
340    while (i--) {
341      Node *n = gnode(h, i);
342      if (!valismarked(gval(n)))  /* value was collected? */
343        removekey(n);  /* remove entry from table */
344    }
345    l = h->gclist;
346  }
347}
348
349
350static void freeobj (lua_State *L, GCObject *o) {
351  switch (o->gch.tt) {
352    case LUA_TPROTO: luaF_freeproto(L, gcotop(o)); break;
353    case LUA_TFUNCTION: luaF_freeclosure(L, gcotocl(o)); break;
354    case LUA_TUPVAL: luaM_freelem(L, gcotouv(o)); break;
355    case LUA_TTABLE: luaH_free(L, gcotoh(o)); break;
356    case LUA_TTHREAD: {
357      lua_assert(gcototh(o) != L && gcototh(o) != G(L)->mainthread);
358      luaE_freethread(L, gcototh(o));
359      break;
360    }
361    case LUA_TSTRING: {
362      luaM_free(L, o, sizestring(gcotots(o)->tsv.len));
363      break;
364    }
365    case LUA_TUSERDATA: {
366      luaM_free(L, o, sizeudata(gcotou(o)->uv.len));
367      break;
368    }
369    default: lua_assert(0);
370  }
371}
372
373
374static int sweeplist (lua_State *L, GCObject **p, int limit) {
375  GCObject *curr;
376  int count = 0;  /* number of collected items */
377  while ((curr = *p) != NULL) {
378    if ((curr->gch.marked & ~(KEYWEAK | VALUEWEAK)) > limit) {
379      unmark(curr);
380      p = &curr->gch.next;
381    }
382    else {
383      count++;
384      *p = curr->gch.next;
385      freeobj(L, curr);
386    }
387  }
388  return count;
389}
390
391
392static void sweepstrings (lua_State *L, int all) {
393  int i;
394  for (i=0; i<G(L)->strt.size; i++) {  /* for each list */
395    G(L)->strt.nuse -= sweeplist(L, &G(L)->strt.hash[i], all);
396  }
397}
398
399
400static void checkSizes (lua_State *L, size_t deadmem) {
401  /* check size of string hash */
402  if (G(L)->strt.nuse < cast(ls_nstr, G(L)->strt.size/4) &&
403      G(L)->strt.size > MINSTRTABSIZE*2)
404    luaS_resize(L, G(L)->strt.size/2);  /* table is too big */
405  /* check size of buffer */
406  if (luaZ_sizebuffer(&G(L)->buff) > LUA_MINBUFFER*2) {  /* buffer too big? */
407    size_t newsize = luaZ_sizebuffer(&G(L)->buff) / 2;
408    luaZ_resizebuffer(L, &G(L)->buff, newsize);
409  }
410  G(L)->GCthreshold = 2*G(L)->nblocks - deadmem;  /* new threshold */
411}
412
413
414static void do1gcTM (lua_State *L, Udata *udata) {
415  const TObject *tm = fasttm(L, udata->uv.metatable, TM_GC);
416  if (tm != NULL) {
417    setobj2s(L->top, tm);
418    setuvalue(L->top+1, udata);
419    L->top += 2;
420    luaD_call(L, L->top - 2, 0);
421  }
422}
423
424
425void luaC_callGCTM (lua_State *L) {
426  lu_byte oldah = L->allowhook;
427  L->allowhook = 0;  /* stop debug hooks during GC tag methods */
428  L->top++;  /* reserve space to keep udata while runs its gc method */
429  while (G(L)->tmudata != NULL) {
430    GCObject *o = G(L)->tmudata;
431    Udata *udata = gcotou(o);
432    G(L)->tmudata = udata->uv.next;  /* remove udata from `tmudata' */
433    udata->uv.next = G(L)->rootudata;  /* return it to `root' list */
434    G(L)->rootudata = o;
435    setuvalue(L->top - 1, udata);  /* keep a reference to it */
436    unmark(o);
437    markfinalized(udata);
438    do1gcTM(L, udata);
439  }
440  L->top--;
441  L->allowhook = oldah;  /* restore hooks */
442}
443
444
445void luaC_sweep (lua_State *L, int all) {
446  if (all) all = 256;  /* larger than any mark */
447  sweeplist(L, &G(L)->rootudata, all);
448  sweepstrings(L, all);
449  sweeplist(L, &G(L)->rootgc, all);
450}
451
452
453/* mark root set */
454static void markroot (GCState *st, lua_State *L) {
455  global_State *g = st->g;
456  markobject(st, defaultmeta(L));
457  markobject(st, registry(L));
458  traversestack(st, g->mainthread);
459  if (L != g->mainthread)  /* another thread is running? */
460    markvalue(st, L);  /* cannot collect it */
461}
462
463
464static size_t mark (lua_State *L) {
465  size_t deadmem;
466  GCState st;
467  GCObject *wkv;
468  st.g = G(L);
469  st.tmark = NULL;
470  st.wkv = st.wk = st.wv = NULL;
471  markroot(&st, L);
472  propagatemarks(&st);  /* mark all reachable objects */
473  cleartablevalues(st.wkv);
474  cleartablevalues(st.wv);
475  wkv = st.wkv;  /* keys must be cleared after preserving udata */
476  st.wkv = NULL;
477  st.wv = NULL;
478  deadmem = luaC_separateudata(L);  /* separate userdata to be preserved */
479  marktmu(&st);  /* mark `preserved' userdata */
480  deadmem += propagatemarks(&st);  /* remark, to propagate `preserveness' */
481  cleartablekeys(wkv);
482  /* `propagatemarks' may resuscitate some weak tables; clear them too */
483  cleartablekeys(st.wk);
484  cleartablevalues(st.wv);
485  cleartablekeys(st.wkv);
486  cleartablevalues(st.wkv);
487  return deadmem;
488}
489
490
491void luaC_collectgarbage (lua_State *L) {
492  size_t deadmem = mark(L);
493  luaC_sweep(L, 0);
494  checkSizes(L, deadmem);
495  luaC_callGCTM(L);
496}
497
498
499void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
500  o->gch.next = G(L)->rootgc;
501  G(L)->rootgc = o;
502  o->gch.marked = 0;
503  o->gch.tt = tt;
504}
505
Note: See TracBrowser for help on using the repository browser.