]> git.lizzy.rs Git - dragonfireclient.git/blob - lib/lua/src/lgc.c
Patch built-in Lua to fix miscompile on Android (#12347)
[dragonfireclient.git] / lib / lua / src / lgc.c
1 /*
2 ** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $
3 ** Garbage Collector
4 ** See Copyright Notice in lua.h
5 */
6
7 #include <string.h>
8
9 #define lgc_c
10 #define LUA_CORE
11
12 #include "lua.h"
13
14 #include "ldebug.h"
15 #include "ldo.h"
16 #include "lfunc.h"
17 #include "lgc.h"
18 #include "lmem.h"
19 #include "lobject.h"
20 #include "lstate.h"
21 #include "lstring.h"
22 #include "ltable.h"
23 #include "ltm.h"
24
25
26 #define GCSTEPSIZE      1024u
27 #define GCSWEEPMAX      40
28 #define GCSWEEPCOST     10
29 #define GCFINALIZECOST  100
30
31
32 #define maskmarks       cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
33
34 #define makewhite(g,x)  \
35    ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))
36
37 #define white2gray(x)   reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
38 #define black2gray(x)   resetbit((x)->gch.marked, BLACKBIT)
39
40 #define stringmark(s)   reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)
41
42
43 #define isfinalized(u)          testbit((u)->marked, FINALIZEDBIT)
44 #define markfinalized(u)        l_setbit((u)->marked, FINALIZEDBIT)
45
46
47 #define KEYWEAK         bitmask(KEYWEAKBIT)
48 #define VALUEWEAK       bitmask(VALUEWEAKBIT)
49
50
51
52 #define markvalue(g,o) { checkconsistency(o); \
53   if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }
54
55 #define markobject(g,t) { if (iswhite(obj2gco(t))) \
56                 reallymarkobject(g, obj2gco(t)); }
57
58
59 #define setthreshold(g)  (g->GCthreshold = (g->estimate/100) * g->gcpause)
60
61
62 static void removeentry (Node *n) {
63   lua_assert(ttisnil(gval(n)));
64   if (iscollectable(gkey(n)))
65     setttype(gkey(n), LUA_TDEADKEY);  /* dead key; remove it */
66 }
67
68
69 static void reallymarkobject (global_State *g, GCObject *o) {
70   lua_assert(iswhite(o) && !isdead(g, o));
71   white2gray(o);
72   switch (o->gch.tt) {
73     case LUA_TSTRING: {
74       return;
75     }
76     case LUA_TUSERDATA: {
77       Table *mt = gco2u(o)->metatable;
78       gray2black(o);  /* udata are never gray */
79       if (mt) markobject(g, mt);
80       markobject(g, gco2u(o)->env);
81       return;
82     }
83     case LUA_TUPVAL: {
84       UpVal *uv = gco2uv(o);
85       markvalue(g, uv->v);
86       if (uv->v == &uv->u.value)  /* closed? */
87         gray2black(o);  /* open upvalues are never black */
88       return;
89     }
90     case LUA_TFUNCTION: {
91       gco2cl(o)->c.gclist = g->gray;
92       g->gray = o;
93       break;
94     }
95     case LUA_TTABLE: {
96       gco2h(o)->gclist = g->gray;
97       g->gray = o;
98       break;
99     }
100     case LUA_TTHREAD: {
101       gco2th(o)->gclist = g->gray;
102       g->gray = o;
103       break;
104     }
105     case LUA_TPROTO: {
106       gco2p(o)->gclist = g->gray;
107       g->gray = o;
108       break;
109     }
110     default: lua_assert(0);
111   }
112 }
113
114
115 static void marktmu (global_State *g) {
116   GCObject *u = g->tmudata;
117   if (u) {
118     do {
119       u = u->gch.next;
120       makewhite(g, u);  /* may be marked, if left from previous GC */
121       reallymarkobject(g, u);
122     } while (u != g->tmudata);
123   }
124 }
125
126
127 /* move `dead' udata that need finalization to list `tmudata' */
128 size_t luaC_separateudata (lua_State *L, int all) {
129   global_State *g = G(L);
130   size_t deadmem = 0;
131   GCObject **p = &g->mainthread->next;
132   GCObject *curr;
133   while ((curr = *p) != NULL) {
134     if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))
135       p = &curr->gch.next;  /* don't bother with them */
136     else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {
137       markfinalized(gco2u(curr));  /* don't need finalization */
138       p = &curr->gch.next;
139     }
140     else {  /* must call its gc method */
141       deadmem += sizeudata(gco2u(curr));
142       markfinalized(gco2u(curr));
143       *p = curr->gch.next;
144       /* link `curr' at the end of `tmudata' list */
145       if (g->tmudata == NULL)  /* list is empty? */
146         g->tmudata = curr->gch.next = curr;  /* creates a circular list */
147       else {
148         curr->gch.next = g->tmudata->gch.next;
149         g->tmudata->gch.next = curr;
150         g->tmudata = curr;
151       }
152     }
153   }
154   return deadmem;
155 }
156
157
158 static int traversetable (global_State *g, Table *h) {
159   int i;
160   int weakkey = 0;
161   int weakvalue = 0;
162   const TValue *mode;
163   if (h->metatable)
164     markobject(g, h->metatable);
165   mode = gfasttm(g, h->metatable, TM_MODE);
166   if (mode && ttisstring(mode)) {  /* is there a weak mode? */
167     // Android's 'FORTIFY libc' calls __builtin_object_size on the argument of strchr.
168     // This produces an incorrect size for the expression `svalue(mode)`, causing
169     // an assertion. By placing it in a temporary, __builtin_object_size returns
170     // -1 (for unknown size) which functions correctly.
171     const char *tmp = svalue(mode);
172     weakkey = (strchr(tmp, 'k') != NULL);
173     weakvalue = (strchr(tmp, 'v') != NULL);
174     if (weakkey || weakvalue) {  /* is really weak? */
175       h->marked &= ~(KEYWEAK | VALUEWEAK);  /* clear bits */
176       h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
177                              (weakvalue << VALUEWEAKBIT));
178       h->gclist = g->weak;  /* must be cleared after GC, ... */
179       g->weak = obj2gco(h);  /* ... so put in the appropriate list */
180     }
181   }
182   if (weakkey && weakvalue) return 1;
183   if (!weakvalue) {
184     i = h->sizearray;
185     while (i--)
186       markvalue(g, &h->array[i]);
187   }
188   i = sizenode(h);
189   while (i--) {
190     Node *n = gnode(h, i);
191     lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
192     if (ttisnil(gval(n)))
193       removeentry(n);  /* remove empty entries */
194     else {
195       lua_assert(!ttisnil(gkey(n)));
196       if (!weakkey) markvalue(g, gkey(n));
197       if (!weakvalue) markvalue(g, gval(n));
198     }
199   }
200   return weakkey || weakvalue;
201 }
202
203
204 /*
205 ** All marks are conditional because a GC may happen while the
206 ** prototype is still being created
207 */
208 static void traverseproto (global_State *g, Proto *f) {
209   int i;
210   if (f->source) stringmark(f->source);
211   for (i=0; i<f->sizek; i++)  /* mark literals */
212     markvalue(g, &f->k[i]);
213   for (i=0; i<f->sizeupvalues; i++) {  /* mark upvalue names */
214     if (f->upvalues[i])
215       stringmark(f->upvalues[i]);
216   }
217   for (i=0; i<f->sizep; i++) {  /* mark nested protos */
218     if (f->p[i])
219       markobject(g, f->p[i]);
220   }
221   for (i=0; i<f->sizelocvars; i++) {  /* mark local-variable names */
222     if (f->locvars[i].varname)
223       stringmark(f->locvars[i].varname);
224   }
225 }
226
227
228
229 static void traverseclosure (global_State *g, Closure *cl) {
230   markobject(g, cl->c.env);
231   if (cl->c.isC) {
232     int i;
233     for (i=0; i<cl->c.nupvalues; i++)  /* mark its upvalues */
234       markvalue(g, &cl->c.upvalue[i]);
235   }
236   else {
237     int i;
238     lua_assert(cl->l.nupvalues == cl->l.p->nups);
239     markobject(g, cl->l.p);
240     for (i=0; i<cl->l.nupvalues; i++)  /* mark its upvalues */
241       markobject(g, cl->l.upvals[i]);
242   }
243 }
244
245
246 static void checkstacksizes (lua_State *L, StkId max) {
247   int ci_used = cast_int(L->ci - L->base_ci);  /* number of `ci' in use */
248   int s_used = cast_int(max - L->stack);  /* part of stack in use */
249   if (L->size_ci > LUAI_MAXCALLS)  /* handling overflow? */
250     return;  /* do not touch the stacks */
251   if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
252     luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */
253   condhardstacktests(luaD_reallocCI(L, ci_used + 1));
254   if (4*s_used < L->stacksize &&
255       2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
256     luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */
257   condhardstacktests(luaD_reallocstack(L, s_used));
258 }
259
260
261 static void traversestack (global_State *g, lua_State *l) {
262   StkId o, lim;
263   CallInfo *ci;
264   markvalue(g, gt(l));
265   lim = l->top;
266   for (ci = l->base_ci; ci <= l->ci; ci++) {
267     lua_assert(ci->top <= l->stack_last);
268     if (lim < ci->top) lim = ci->top;
269   }
270   for (o = l->stack; o < l->top; o++)
271     markvalue(g, o);
272   for (; o <= lim; o++)
273     setnilvalue(o);
274   checkstacksizes(l, lim);
275 }
276
277
278 /*
279 ** traverse one gray object, turning it to black.
280 ** Returns `quantity' traversed.
281 */
282 static l_mem propagatemark (global_State *g) {
283   GCObject *o = g->gray;
284   lua_assert(isgray(o));
285   gray2black(o);
286   switch (o->gch.tt) {
287     case LUA_TTABLE: {
288       Table *h = gco2h(o);
289       g->gray = h->gclist;
290       if (traversetable(g, h))  /* table is weak? */
291         black2gray(o);  /* keep it gray */
292       return sizeof(Table) + sizeof(TValue) * h->sizearray +
293                              sizeof(Node) * sizenode(h);
294     }
295     case LUA_TFUNCTION: {
296       Closure *cl = gco2cl(o);
297       g->gray = cl->c.gclist;
298       traverseclosure(g, cl);
299       return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
300                            sizeLclosure(cl->l.nupvalues);
301     }
302     case LUA_TTHREAD: {
303       lua_State *th = gco2th(o);
304       g->gray = th->gclist;
305       th->gclist = g->grayagain;
306       g->grayagain = o;
307       black2gray(o);
308       traversestack(g, th);
309       return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
310                                  sizeof(CallInfo) * th->size_ci;
311     }
312     case LUA_TPROTO: {
313       Proto *p = gco2p(o);
314       g->gray = p->gclist;
315       traverseproto(g, p);
316       return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
317                              sizeof(Proto *) * p->sizep +
318                              sizeof(TValue) * p->sizek + 
319                              sizeof(int) * p->sizelineinfo +
320                              sizeof(LocVar) * p->sizelocvars +
321                              sizeof(TString *) * p->sizeupvalues;
322     }
323     default: lua_assert(0); return 0;
324   }
325 }
326
327
328 static size_t propagateall (global_State *g) {
329   size_t m = 0;
330   while (g->gray) m += propagatemark(g);
331   return m;
332 }
333
334
335 /*
336 ** The next function tells whether a key or value can be cleared from
337 ** a weak table. Non-collectable objects are never removed from weak
338 ** tables. Strings behave as `values', so are never removed too. for
339 ** other objects: if really collected, cannot keep them; for userdata
340 ** being finalized, keep them in keys, but not in values
341 */
342 static int iscleared (const TValue *o, int iskey) {
343   if (!iscollectable(o)) return 0;
344   if (ttisstring(o)) {
345     stringmark(rawtsvalue(o));  /* strings are `values', so are never weak */
346     return 0;
347   }
348   return iswhite(gcvalue(o)) ||
349     (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
350 }
351
352
353 /*
354 ** clear collected entries from weaktables
355 */
356 static void cleartable (GCObject *l) {
357   while (l) {
358     Table *h = gco2h(l);
359     int i = h->sizearray;
360     lua_assert(testbit(h->marked, VALUEWEAKBIT) ||
361                testbit(h->marked, KEYWEAKBIT));
362     if (testbit(h->marked, VALUEWEAKBIT)) {
363       while (i--) {
364         TValue *o = &h->array[i];
365         if (iscleared(o, 0))  /* value was collected? */
366           setnilvalue(o);  /* remove value */
367       }
368     }
369     i = sizenode(h);
370     while (i--) {
371       Node *n = gnode(h, i);
372       if (!ttisnil(gval(n)) &&  /* non-empty entry? */
373           (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {
374         setnilvalue(gval(n));  /* remove value ... */
375         removeentry(n);  /* remove entry from table */
376       }
377     }
378     l = h->gclist;
379   }
380 }
381
382
383 static void freeobj (lua_State *L, GCObject *o) {
384   switch (o->gch.tt) {
385     case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
386     case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
387     case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
388     case LUA_TTABLE: luaH_free(L, gco2h(o)); break;
389     case LUA_TTHREAD: {
390       lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);
391       luaE_freethread(L, gco2th(o));
392       break;
393     }
394     case LUA_TSTRING: {
395       G(L)->strt.nuse--;
396       luaM_freemem(L, o, sizestring(gco2ts(o)));
397       break;
398     }
399     case LUA_TUSERDATA: {
400       luaM_freemem(L, o, sizeudata(gco2u(o)));
401       break;
402     }
403     default: lua_assert(0);
404   }
405 }
406
407
408
409 #define sweepwholelist(L,p)     sweeplist(L,p,MAX_LUMEM)
410
411
412 static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
413   GCObject *curr;
414   global_State *g = G(L);
415   int deadmask = otherwhite(g);
416   while ((curr = *p) != NULL && count-- > 0) {
417     if (curr->gch.tt == LUA_TTHREAD)  /* sweep open upvalues of each thread */
418       sweepwholelist(L, &gco2th(curr)->openupval);
419     if ((curr->gch.marked ^ WHITEBITS) & deadmask) {  /* not dead? */
420       lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
421       makewhite(g, curr);  /* make it white (for next cycle) */
422       p = &curr->gch.next;
423     }
424     else {  /* must erase `curr' */
425       lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
426       *p = curr->gch.next;
427       if (curr == g->rootgc)  /* is the first element of the list? */
428         g->rootgc = curr->gch.next;  /* adjust first */
429       freeobj(L, curr);
430     }
431   }
432   return p;
433 }
434
435
436 static void checkSizes (lua_State *L) {
437   global_State *g = G(L);
438   /* check size of string hash */
439   if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
440       g->strt.size > MINSTRTABSIZE*2)
441     luaS_resize(L, g->strt.size/2);  /* table is too big */
442   /* check size of buffer */
443   if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) {  /* buffer too big? */
444     size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
445     luaZ_resizebuffer(L, &g->buff, newsize);
446   }
447 }
448
449
450 static void GCTM (lua_State *L) {
451   global_State *g = G(L);
452   GCObject *o = g->tmudata->gch.next;  /* get first element */
453   Udata *udata = rawgco2u(o);
454   const TValue *tm;
455   /* remove udata from `tmudata' */
456   if (o == g->tmudata)  /* last element? */
457     g->tmudata = NULL;
458   else
459     g->tmudata->gch.next = udata->uv.next;
460   udata->uv.next = g->mainthread->next;  /* return it to `root' list */
461   g->mainthread->next = o;
462   makewhite(g, o);
463   tm = fasttm(L, udata->uv.metatable, TM_GC);
464   if (tm != NULL) {
465     lu_byte oldah = L->allowhook;
466     lu_mem oldt = g->GCthreshold;
467     L->allowhook = 0;  /* stop debug hooks during GC tag method */
468     g->GCthreshold = 2*g->totalbytes;  /* avoid GC steps */
469     setobj2s(L, L->top, tm);
470     setuvalue(L, L->top+1, udata);
471     L->top += 2;
472     luaD_call(L, L->top - 2, 0);
473     L->allowhook = oldah;  /* restore hooks */
474     g->GCthreshold = oldt;  /* restore threshold */
475   }
476 }
477
478
479 /*
480 ** Call all GC tag methods
481 */
482 void luaC_callGCTM (lua_State *L) {
483   while (G(L)->tmudata)
484     GCTM(L);
485 }
486
487
488 void luaC_freeall (lua_State *L) {
489   global_State *g = G(L);
490   int i;
491   g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT);  /* mask to collect all elements */
492   sweepwholelist(L, &g->rootgc);
493   for (i = 0; i < g->strt.size; i++)  /* free all string lists */
494     sweepwholelist(L, &g->strt.hash[i]);
495 }
496
497
498 static void markmt (global_State *g) {
499   int i;
500   for (i=0; i<NUM_TAGS; i++)
501     if (g->mt[i]) markobject(g, g->mt[i]);
502 }
503
504
505 /* mark root set */
506 static void markroot (lua_State *L) {
507   global_State *g = G(L);
508   g->gray = NULL;
509   g->grayagain = NULL;
510   g->weak = NULL;
511   markobject(g, g->mainthread);
512   /* make global table be traversed before main stack */
513   markvalue(g, gt(g->mainthread));
514   markvalue(g, registry(L));
515   markmt(g);
516   g->gcstate = GCSpropagate;
517 }
518
519
520 static void remarkupvals (global_State *g) {
521   UpVal *uv;
522   for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
523     lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
524     if (isgray(obj2gco(uv)))
525       markvalue(g, uv->v);
526   }
527 }
528
529
530 static void atomic (lua_State *L) {
531   global_State *g = G(L);
532   size_t udsize;  /* total size of userdata to be finalized */
533   /* remark occasional upvalues of (maybe) dead threads */
534   remarkupvals(g);
535   /* traverse objects cautch by write barrier and by 'remarkupvals' */
536   propagateall(g);
537   /* remark weak tables */
538   g->gray = g->weak;
539   g->weak = NULL;
540   lua_assert(!iswhite(obj2gco(g->mainthread)));
541   markobject(g, L);  /* mark running thread */
542   markmt(g);  /* mark basic metatables (again) */
543   propagateall(g);
544   /* remark gray again */
545   g->gray = g->grayagain;
546   g->grayagain = NULL;
547   propagateall(g);
548   udsize = luaC_separateudata(L, 0);  /* separate userdata to be finalized */
549   marktmu(g);  /* mark `preserved' userdata */
550   udsize += propagateall(g);  /* remark, to propagate `preserveness' */
551   cleartable(g->weak);  /* remove collected objects from weak tables */
552   /* flip current white */
553   g->currentwhite = cast_byte(otherwhite(g));
554   g->sweepstrgc = 0;
555   g->sweepgc = &g->rootgc;
556   g->gcstate = GCSsweepstring;
557   g->estimate = g->totalbytes - udsize;  /* first estimate */
558 }
559
560
561 static l_mem singlestep (lua_State *L) {
562   global_State *g = G(L);
563   /*lua_checkmemory(L);*/
564   switch (g->gcstate) {
565     case GCSpause: {
566       markroot(L);  /* start a new collection */
567       return 0;
568     }
569     case GCSpropagate: {
570       if (g->gray)
571         return propagatemark(g);
572       else {  /* no more `gray' objects */
573         atomic(L);  /* finish mark phase */
574         return 0;
575       }
576     }
577     case GCSsweepstring: {
578       lu_mem old = g->totalbytes;
579       sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
580       if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */
581         g->gcstate = GCSsweep;  /* end sweep-string phase */
582       lua_assert(old >= g->totalbytes);
583       g->estimate -= old - g->totalbytes;
584       return GCSWEEPCOST;
585     }
586     case GCSsweep: {
587       lu_mem old = g->totalbytes;
588       g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
589       if (*g->sweepgc == NULL) {  /* nothing more to sweep? */
590         checkSizes(L);
591         g->gcstate = GCSfinalize;  /* end sweep phase */
592       }
593       lua_assert(old >= g->totalbytes);
594       g->estimate -= old - g->totalbytes;
595       return GCSWEEPMAX*GCSWEEPCOST;
596     }
597     case GCSfinalize: {
598       if (g->tmudata) {
599         GCTM(L);
600         if (g->estimate > GCFINALIZECOST)
601           g->estimate -= GCFINALIZECOST;
602         return GCFINALIZECOST;
603       }
604       else {
605         g->gcstate = GCSpause;  /* end collection */
606         g->gcdept = 0;
607         return 0;
608       }
609     }
610     default: lua_assert(0); return 0;
611   }
612 }
613
614
615 void luaC_step (lua_State *L) {
616   global_State *g = G(L);
617   l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
618   if (lim == 0)
619     lim = (MAX_LUMEM-1)/2;  /* no limit */
620   g->gcdept += g->totalbytes - g->GCthreshold;
621   do {
622     lim -= singlestep(L);
623     if (g->gcstate == GCSpause)
624       break;
625   } while (lim > 0);
626   if (g->gcstate != GCSpause) {
627     if (g->gcdept < GCSTEPSIZE)
628       g->GCthreshold = g->totalbytes + GCSTEPSIZE;  /* - lim/g->gcstepmul;*/
629     else {
630       g->gcdept -= GCSTEPSIZE;
631       g->GCthreshold = g->totalbytes;
632     }
633   }
634   else {
635     setthreshold(g);
636   }
637 }
638
639
640 void luaC_fullgc (lua_State *L) {
641   global_State *g = G(L);
642   if (g->gcstate <= GCSpropagate) {
643     /* reset sweep marks to sweep all elements (returning them to white) */
644     g->sweepstrgc = 0;
645     g->sweepgc = &g->rootgc;
646     /* reset other collector lists */
647     g->gray = NULL;
648     g->grayagain = NULL;
649     g->weak = NULL;
650     g->gcstate = GCSsweepstring;
651   }
652   lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);
653   /* finish any pending sweep phase */
654   while (g->gcstate != GCSfinalize) {
655     lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
656     singlestep(L);
657   }
658   markroot(L);
659   while (g->gcstate != GCSpause) {
660     singlestep(L);
661   }
662   setthreshold(g);
663 }
664
665
666 void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
667   global_State *g = G(L);
668   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
669   lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
670   lua_assert(ttype(&o->gch) != LUA_TTABLE);
671   /* must keep invariant? */
672   if (g->gcstate == GCSpropagate)
673     reallymarkobject(g, v);  /* restore invariant */
674   else  /* don't mind */
675     makewhite(g, o);  /* mark as white just to avoid other barriers */
676 }
677
678
679 void luaC_barrierback (lua_State *L, Table *t) {
680   global_State *g = G(L);
681   GCObject *o = obj2gco(t);
682   lua_assert(isblack(o) && !isdead(g, o));
683   lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
684   black2gray(o);  /* make table gray (again) */
685   t->gclist = g->grayagain;
686   g->grayagain = o;
687 }
688
689
690 void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
691   global_State *g = G(L);
692   o->gch.next = g->rootgc;
693   g->rootgc = o;
694   o->gch.marked = luaC_white(g);
695   o->gch.tt = tt;
696 }
697
698
699 void luaC_linkupval (lua_State *L, UpVal *uv) {
700   global_State *g = G(L);
701   GCObject *o = obj2gco(uv);
702   o->gch.next = g->rootgc;  /* link upvalue into `rootgc' list */
703   g->rootgc = o;
704   if (isgray(o)) { 
705     if (g->gcstate == GCSpropagate) {
706       gray2black(o);  /* closed upvalues need barrier */
707       luaC_barrier(L, uv, uv->v);
708     }
709     else {  /* sweep phase: sweep it (turning it into white) */
710       makewhite(g, o);
711       lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
712     }
713   }
714 }
715