diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2002-08-30 16:09:21 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2002-08-30 16:09:21 -0300 |
| commit | fdafd4f4a88d1584dea900517445a17d87f8b82f (patch) | |
| tree | 52e38e886d569e77c1df82ebe2bcfbaafd4c6f75 /lgc.c | |
| parent | beeff4ccafe5877d00119cb3d93f1f937d46dcfb (diff) | |
| download | lua-fdafd4f4a88d1584dea900517445a17d87f8b82f.tar.gz lua-fdafd4f4a88d1584dea900517445a17d87f8b82f.tar.bz2 lua-fdafd4f4a88d1584dea900517445a17d87f8b82f.zip | |
new structure for collectable objects, sharing a common header
Diffstat (limited to 'lgc.c')
| -rw-r--r-- | lgc.c | 279 |
1 files changed, 103 insertions, 176 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lgc.c,v 1.146 2002/08/16 14:45:55 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 1.147 2002/08/16 20:00:28 roberto Exp roberto $ |
| 3 | ** Garbage Collector | 3 | ** Garbage Collector |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -34,28 +34,24 @@ typedef struct GCState { | |||
| 34 | #define resetbit(x,b) ((x) &= cast(lu_byte, ~(1<<(b)))) | 34 | #define resetbit(x,b) ((x) &= cast(lu_byte, ~(1<<(b)))) |
| 35 | #define testbit(x,b) ((x) & (1<<(b))) | 35 | #define testbit(x,b) ((x) & (1<<(b))) |
| 36 | 36 | ||
| 37 | #define mark(x) setbit((x)->gch.marked, 0) | ||
| 38 | #define unmark(x) resetbit((x)->gch.marked, 0) | ||
| 39 | #define ismarked(x) ((x)->gch.marked & ((1<<4)|1)) | ||
| 37 | 40 | ||
| 38 | #define strmark(s) setbit((s)->tsv.marked, 0) | 41 | #define strmark(s) setbit((s)->tsv.marked, 0) |
| 39 | #define strunmark(s) resetbit((s)->tsv.marked, 0) | 42 | #define strunmark(s) resetbit((s)->tsv.marked, 0) |
| 40 | 43 | ||
| 41 | 44 | ||
| 45 | #define isfinalized(u) (!testbit((u)->uv.marked, 1)) | ||
| 46 | #define markfinalized(u) resetbit((u)->uv.marked, 1) | ||
| 42 | 47 | ||
| 43 | /* mark tricks for userdata */ | ||
| 44 | #define isudmarked(u) testbit(u->uv.marked, 0) | ||
| 45 | #define markud(u) setbit(u->uv.marked, 0) | ||
| 46 | #define unmarkud(u) resetbit(u->uv.marked, 0) | ||
| 47 | 48 | ||
| 48 | #define isfinalized(u) testbit(u->uv.marked, 1) | 49 | static void reallymarkobject (GCState *st, GCObject *o); |
| 49 | #define markfinalized(u) setbit(u->uv.marked, 1) | ||
| 50 | 50 | ||
| 51 | #define markobject(st,o) \ | ||
| 52 | if (iscollectable(o)) reallymarkobject(st,(o)->value.gc) | ||
| 51 | 53 | ||
| 52 | #define ismarkable(o) (!((1 << ttype(o)) & \ | 54 | #define marktable(st,t) reallymarkobject(st, cast(GCObject *, (t))) |
| 53 | ((1 << LUA_TNIL) | (1 << LUA_TNUMBER) | \ | ||
| 54 | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA)))) | ||
| 55 | |||
| 56 | static void reallymarkobject (GCState *st, TObject *o); | ||
| 57 | |||
| 58 | #define markobject(st,o) if (ismarkable(o)) reallymarkobject(st,o) | ||
| 59 | 55 | ||
| 60 | 56 | ||
| 61 | static void markproto (Proto *f) { | 57 | static void markproto (Proto *f) { |
| @@ -76,63 +72,46 @@ static void markproto (Proto *f) { | |||
| 76 | } | 72 | } |
| 77 | 73 | ||
| 78 | 74 | ||
| 79 | static void marktable (GCState *st, Table *h) { | ||
| 80 | if (!h->marked) { | ||
| 81 | h->marked = 1; | ||
| 82 | h->gclist = st->tmark; /* chain it for later traversal */ | ||
| 83 | st->tmark = h; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | 75 | ||
| 88 | static void markclosure (GCState *st, Closure *cl) { | 76 | static void markclosure (GCState *st, Closure *cl) { |
| 89 | if (!cl->c.marked) { | 77 | if (cl->c.isC) { |
| 90 | cl->c.marked = 1; | 78 | int i; |
| 91 | if (cl->c.isC) { | 79 | for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ |
| 92 | int i; | 80 | markobject(st, &cl->c.upvalue[i]); |
| 93 | for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ | 81 | } |
| 94 | markobject(st, &cl->c.upvalue[i]); | 82 | else { |
| 95 | } | 83 | int i; |
| 96 | else { | 84 | lua_assert(cl->l.nupvalues == cl->l.p->nupvalues); |
| 97 | int i; | 85 | marktable(st, hvalue(&cl->l.g)); |
| 98 | lua_assert(cl->l.nupvalues == cl->l.p->nupvalues); | 86 | markproto(cl->l.p); |
| 99 | marktable(st, hvalue(&cl->l.g)); | 87 | for (i=0; i<cl->l.nupvalues; i++) { /* mark its upvalues */ |
| 100 | markproto(cl->l.p); | 88 | UpVal *u = cl->l.upvals[i]; |
| 101 | for (i=0; i<cl->l.nupvalues; i++) { /* mark its upvalues */ | 89 | if (!u->marked) { |
| 102 | UpVal *u = cl->l.upvals[i]; | 90 | markobject(st, &u->value); |
| 103 | if (!u->marked) { | 91 | u->marked = 1; |
| 104 | markobject(st, &u->value); | ||
| 105 | u->marked = 1; | ||
| 106 | } | ||
| 107 | } | 92 | } |
| 108 | } | 93 | } |
| 109 | } | 94 | } |
| 110 | } | 95 | } |
| 111 | 96 | ||
| 112 | 97 | ||
| 113 | static void markudata (GCState *st, Udata *u) { | 98 | static void reallymarkobject (GCState *st, GCObject *o) { |
| 114 | markud(u); | 99 | if (ismarked(o)) return; |
| 115 | marktable(st, u->uv.metatable); | 100 | mark(o); |
| 116 | } | 101 | switch (o->gch.tt) { |
| 117 | 102 | case LUA_TFUNCTION: { | |
| 118 | 103 | markclosure(st, &o->cl); | |
| 119 | static void reallymarkobject (GCState *st, TObject *o) { | ||
| 120 | switch (ttype(o)) { | ||
| 121 | case LUA_TSTRING: | ||
| 122 | strmark(tsvalue(o)); | ||
| 123 | break; | ||
| 124 | case LUA_TUSERDATA: | ||
| 125 | if (!isudmarked(uvalue(o))) | ||
| 126 | markudata(st, uvalue(o)); | ||
| 127 | break; | 104 | break; |
| 128 | case LUA_TFUNCTION: | 105 | } |
| 129 | markclosure(st, clvalue(o)); | 106 | case LUA_TUSERDATA: { |
| 107 | marktable(st, (&o->u)->uv.metatable); | ||
| 130 | break; | 108 | break; |
| 109 | } | ||
| 131 | case LUA_TTABLE: { | 110 | case LUA_TTABLE: { |
| 132 | marktable(st, hvalue(o)); | 111 | (&o->h)->gclist = st->tmark; /* chain it for later traversal */ |
| 112 | st->tmark = &o->h; | ||
| 133 | break; | 113 | break; |
| 134 | } | 114 | } |
| 135 | default: lua_assert(0); /* should not be called with other types */ | ||
| 136 | } | 115 | } |
| 137 | } | 116 | } |
| 138 | 117 | ||
| @@ -177,22 +156,29 @@ static void traversestacks (GCState *st) { | |||
| 177 | 156 | ||
| 178 | 157 | ||
| 179 | static void marktmu (GCState *st) { | 158 | static void marktmu (GCState *st) { |
| 180 | Udata *u; | 159 | GCObject *u; |
| 181 | for (u = G(st->L)->tmudata; u; u = u->uv.next) | 160 | for (u = G(st->L)->tmudata; u; u = u->uv.next) { |
| 182 | markudata(st, u); | 161 | mark(u); |
| 162 | marktable(st, (&u->u)->uv.metatable); | ||
| 163 | } | ||
| 183 | } | 164 | } |
| 184 | 165 | ||
| 185 | 166 | ||
| 186 | /* move `dead' udata that need finalization to list `tmudata' */ | 167 | /* move `dead' udata that need finalization to list `tmudata' */ |
| 187 | static void separateudata (lua_State *L) { | 168 | static void separateudata (lua_State *L) { |
| 188 | Udata **p = &G(L)->rootudata; | 169 | GCObject **p = &G(L)->rootudata; |
| 189 | Udata *curr; | 170 | GCObject *curr; |
| 190 | Udata *collected = NULL; /* to collect udata with gc event */ | 171 | GCObject *collected = NULL; /* to collect udata with gc event */ |
| 191 | Udata **lastcollected = &collected; | 172 | GCObject **lastcollected = &collected; |
| 192 | while ((curr = *p) != NULL) { | 173 | while ((curr = *p) != NULL) { |
| 193 | if (isudmarked(curr) || isfinalized(curr) || | 174 | lua_assert(curr->gch.tt == LUA_TUSERDATA); |
| 194 | (fasttm(L, curr->uv.metatable, TM_GC) == NULL)) | 175 | if (ismarked(curr) || isfinalized(&curr->u)) |
| 176 | p = &curr->uv.next; /* don't bother with them */ | ||
| 177 | |||
| 178 | else if (fasttm(L, (&curr->u)->uv.metatable, TM_GC) == NULL) { | ||
| 179 | markfinalized(&curr->u); /* don't need finalization */ | ||
| 195 | p = &curr->uv.next; | 180 | p = &curr->uv.next; |
| 181 | } | ||
| 196 | else { /* must call its gc method */ | 182 | else { /* must call its gc method */ |
| 197 | *p = curr->uv.next; | 183 | *p = curr->uv.next; |
| 198 | curr->uv.next = NULL; /* link `curr' at the end of `collected' list */ | 184 | curr->uv.next = NULL; /* link `curr' at the end of `collected' list */ |
| @@ -208,7 +194,7 @@ static void separateudata (lua_State *L) { | |||
| 208 | 194 | ||
| 209 | static void removekey (Node *n) { | 195 | static void removekey (Node *n) { |
| 210 | setnilvalue(val(n)); /* remove corresponding value ... */ | 196 | setnilvalue(val(n)); /* remove corresponding value ... */ |
| 211 | if (ismarkable(key(n))) | 197 | if (iscollectable(key(n))) |
| 212 | setttype(key(n), LUA_TNONE); /* dead key; remove it */ | 198 | setttype(key(n), LUA_TNONE); /* dead key; remove it */ |
| 213 | } | 199 | } |
| 214 | 200 | ||
| @@ -251,20 +237,10 @@ static void propagatemarks (GCState *st) { | |||
| 251 | } | 237 | } |
| 252 | 238 | ||
| 253 | 239 | ||
| 254 | static int ismarked (const TObject *o) { | 240 | static int valismarked (const TObject *o) { |
| 255 | switch (ttype(o)) { | 241 | if (ttisstring(o)) |
| 256 | case LUA_TUSERDATA: | 242 | strmark(tsvalue(o)); /* strings are `values', so are never weak */ |
| 257 | return isudmarked(uvalue(o)); | 243 | return !iscollectable(o) || testbit(o->value.gc->gch.marked, 0); |
| 258 | case LUA_TTABLE: | ||
| 259 | return hvalue(o)->marked; | ||
| 260 | case LUA_TFUNCTION: | ||
| 261 | return clvalue(o)->c.marked; | ||
| 262 | case LUA_TSTRING: | ||
| 263 | strmark(tsvalue(o)); /* strings are `values', so are never weak */ | ||
| 264 | /* go through */ | ||
| 265 | default: /* number, nil, boolean, udataval */ | ||
| 266 | return 1; | ||
| 267 | } | ||
| 268 | } | 244 | } |
| 269 | 245 | ||
| 270 | 246 | ||
| @@ -279,7 +255,7 @@ static void cleartablekeys (GCState *st) { | |||
| 279 | int i = sizenode(h); | 255 | int i = sizenode(h); |
| 280 | while (i--) { | 256 | while (i--) { |
| 281 | Node *n = node(h, i); | 257 | Node *n = node(h, i); |
| 282 | if (!ismarked(key(n))) /* key was collected? */ | 258 | if (!valismarked(key(n))) /* key was collected? */ |
| 283 | removekey(n); /* remove entry from table */ | 259 | removekey(n); /* remove entry from table */ |
| 284 | } | 260 | } |
| 285 | } | 261 | } |
| @@ -297,13 +273,13 @@ static void cleartablevalues (GCState *st) { | |||
| 297 | int i = sizearray(h); | 273 | int i = sizearray(h); |
| 298 | while (i--) { | 274 | while (i--) { |
| 299 | TObject *o = &h->array[i]; | 275 | TObject *o = &h->array[i]; |
| 300 | if (!ismarked(o)) /* value was collected? */ | 276 | if (!valismarked(o)) /* value was collected? */ |
| 301 | setnilvalue(o); /* remove value */ | 277 | setnilvalue(o); /* remove value */ |
| 302 | } | 278 | } |
| 303 | i = sizenode(h); | 279 | i = sizenode(h); |
| 304 | while (i--) { | 280 | while (i--) { |
| 305 | Node *n = node(h, i); | 281 | Node *n = node(h, i); |
| 306 | if (!ismarked(val(n))) /* value was collected? */ | 282 | if (!valismarked(val(n))) /* value was collected? */ |
| 307 | removekey(n); /* remove entry from table */ | 283 | removekey(n); /* remove entry from table */ |
| 308 | } | 284 | } |
| 309 | } | 285 | } |
| @@ -311,103 +287,47 @@ static void cleartablevalues (GCState *st) { | |||
| 311 | } | 287 | } |
| 312 | 288 | ||
| 313 | 289 | ||
| 314 | static void sweepproto (lua_State *L) { | 290 | static void freeobj (lua_State *L, GCObject *o) { |
| 315 | Proto **p = &G(L)->rootproto; | 291 | switch (o->gch.tt) { |
| 316 | Proto *curr; | 292 | case LUA_TPROTO: luaF_freeproto(L, &o->p); break; |
| 317 | while ((curr = *p) != NULL) { | 293 | case LUA_TFUNCTION: luaF_freeclosure(L, &o->cl); break; |
| 318 | if (curr->marked) { | 294 | case LUA_TUPVAL: luaM_freelem(L, &o->uv); break; |
| 319 | curr->marked = 0; | 295 | case LUA_TTABLE: luaH_free(L, &o->h); break; |
| 320 | p = &curr->next; | 296 | case LUA_TSTRING: { |
| 321 | } | 297 | luaM_free(L, o, sizestring((&o->ts)->tsv.len)); |
| 322 | else { | 298 | break; |
| 323 | *p = curr->next; | ||
| 324 | luaF_freeproto(L, curr); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | |||
| 330 | static void sweepclosures (lua_State *L) { | ||
| 331 | Closure **p = &G(L)->rootcl; | ||
| 332 | Closure *curr; | ||
| 333 | while ((curr = *p) != NULL) { | ||
| 334 | if (curr->c.marked) { | ||
| 335 | curr->c.marked = 0; | ||
| 336 | p = &curr->c.next; | ||
| 337 | } | ||
| 338 | else { | ||
| 339 | *p = curr->c.next; | ||
| 340 | luaF_freeclosure(L, curr); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | |||
| 346 | static void sweepupval (lua_State *L) { | ||
| 347 | UpVal **v = &G(L)->rootupval; | ||
| 348 | UpVal *curr; | ||
| 349 | while ((curr = *v) != NULL) { | ||
| 350 | if (curr->marked) { | ||
| 351 | curr->marked = 0; /* unmark */ | ||
| 352 | v = &curr->next; /* next */ | ||
| 353 | } | ||
| 354 | else { | ||
| 355 | *v = curr->next; /* next */ | ||
| 356 | luaM_freelem(L, curr); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | static void sweeptable (lua_State *L) { | ||
| 363 | Table **p = &G(L)->roottable; | ||
| 364 | Table *curr; | ||
| 365 | while ((curr = *p) != NULL) { | ||
| 366 | if (curr->marked) { | ||
| 367 | curr->marked = 0; | ||
| 368 | p = &curr->next; | ||
| 369 | } | 299 | } |
| 370 | else { | 300 | case LUA_TUSERDATA: { |
| 371 | *p = curr->next; | 301 | luaM_free(L, o, sizeudata((&o->u)->uv.len)); |
| 372 | luaH_free(L, curr); | 302 | break; |
| 373 | } | 303 | } |
| 304 | default: lua_assert(0); | ||
| 374 | } | 305 | } |
| 375 | } | 306 | } |
| 376 | 307 | ||
| 377 | 308 | ||
| 378 | 309 | static int sweeplist (lua_State *L, GCObject **p, int limit) { | |
| 379 | static void sweepudata (lua_State *L) { | 310 | GCObject *curr; |
| 380 | Udata **p = &G(L)->rootudata; | 311 | int count = 0; /* number of collected items */ |
| 381 | Udata *curr; | ||
| 382 | while ((curr = *p) != NULL) { | 312 | while ((curr = *p) != NULL) { |
| 383 | if (isudmarked(curr)) { | 313 | if (curr->gch.marked > limit) { |
| 384 | unmarkud(curr); | 314 | unmark(curr); |
| 385 | p = &curr->uv.next; | 315 | p = &curr->gch.next; |
| 386 | } | 316 | } |
| 387 | else { | 317 | else { |
| 388 | *p = curr->uv.next; | 318 | count++; |
| 389 | luaM_free(L, curr, sizeudata(curr->uv.len)); | 319 | *p = curr->gch.next; |
| 320 | freeobj(L, curr); | ||
| 390 | } | 321 | } |
| 391 | } | 322 | } |
| 323 | return count; | ||
| 392 | } | 324 | } |
| 393 | 325 | ||
| 394 | 326 | ||
| 395 | static void sweepstrings (lua_State *L, int all) { | 327 | static void sweepstrings (lua_State *L, int all) { |
| 396 | int i; | 328 | int i; |
| 397 | for (i=0; i<G(L)->strt.size; i++) { /* for each list */ | 329 | for (i=0; i<G(L)->strt.size; i++) { /* for each list */ |
| 398 | TString **p = &G(L)->strt.hash[i]; | 330 | G(L)->strt.nuse -= sweeplist(L, &G(L)->strt.hash[i], all); |
| 399 | TString *curr; | ||
| 400 | while ((curr = *p) != NULL) { | ||
| 401 | if (curr->tsv.marked && !all) { /* preserve? */ | ||
| 402 | strunmark(curr); | ||
| 403 | p = &curr->tsv.nexthash; | ||
| 404 | } | ||
| 405 | else { /* collect */ | ||
| 406 | *p = curr->tsv.nexthash; | ||
| 407 | G(L)->strt.nuse--; | ||
| 408 | luaM_free(L, curr, sizestring(curr->tsv.len)); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | } | 331 | } |
| 412 | if (G(L)->strt.nuse < cast(ls_nstr, G(L)->strt.size/4) && | 332 | if (G(L)->strt.nuse < cast(ls_nstr, G(L)->strt.size/4) && |
| 413 | G(L)->strt.size > MINSTRTABSIZE*2) | 333 | G(L)->strt.size > MINSTRTABSIZE*2) |
| @@ -442,14 +362,14 @@ static void callGCTM (lua_State *L) { | |||
| 442 | setallowhook(L, 0); /* stop debug hooks during GC tag methods */ | 362 | setallowhook(L, 0); /* stop debug hooks during GC tag methods */ |
| 443 | L->top++; /* reserve space to keep udata while runs its gc method */ | 363 | L->top++; /* reserve space to keep udata while runs its gc method */ |
| 444 | while (G(L)->tmudata != NULL) { | 364 | while (G(L)->tmudata != NULL) { |
| 445 | Udata *udata = G(L)->tmudata; | 365 | GCObject *udata = G(L)->tmudata; |
| 446 | G(L)->tmudata = udata->uv.next; /* remove udata from `tmudata' */ | 366 | G(L)->tmudata = udata->uv.next; /* remove udata from `tmudata' */ |
| 447 | udata->uv.next = G(L)->rootudata; /* return it to `root' list */ | 367 | udata->uv.next = G(L)->rootudata; /* return it to `root' list */ |
| 448 | G(L)->rootudata = udata; | 368 | G(L)->rootudata = udata; |
| 449 | setuvalue(L->top - 1, udata); /* keep a reference to it */ | 369 | setuvalue(L->top - 1, &udata->u); /* keep a reference to it */ |
| 450 | unmarkud(udata); | 370 | unmark(udata); |
| 451 | markfinalized(udata); | 371 | markfinalized(&udata->u); |
| 452 | do1gcTM(L, udata); | 372 | do1gcTM(L, &udata->u); |
| 453 | } | 373 | } |
| 454 | L->top--; | 374 | L->top--; |
| 455 | setallowhook(L, oldah); /* restore hooks */ | 375 | setallowhook(L, oldah); /* restore hooks */ |
| @@ -463,12 +383,10 @@ void luaC_callallgcTM (lua_State *L) { | |||
| 463 | 383 | ||
| 464 | 384 | ||
| 465 | void luaC_sweep (lua_State *L, int all) { | 385 | void luaC_sweep (lua_State *L, int all) { |
| 466 | sweepudata(L); | 386 | if (all) all = 256; /* larger than any mark */ |
| 387 | sweeplist(L, &G(L)->rootudata, all); | ||
| 467 | sweepstrings(L, all); | 388 | sweepstrings(L, all); |
| 468 | sweeptable(L); | 389 | sweeplist(L, &G(L)->rootgc, all); |
| 469 | sweepproto(L); | ||
| 470 | sweepupval(L); | ||
| 471 | sweepclosures(L); | ||
| 472 | } | 390 | } |
| 473 | 391 | ||
| 474 | 392 | ||
| @@ -482,7 +400,8 @@ void luaC_collectgarbage (lua_State *L) { | |||
| 482 | cleartablevalues(&st); | 400 | cleartablevalues(&st); |
| 483 | separateudata(L); /* separate userdata to be preserved */ | 401 | separateudata(L); /* separate userdata to be preserved */ |
| 484 | marktmu(&st); /* mark `preserved' userdata */ | 402 | marktmu(&st); /* mark `preserved' userdata */ |
| 485 | propagatemarks(&st); /* remark */ | 403 | propagatemarks(&st); /* remark, to propagate `preserveness' */ |
| 404 | cleartablevalues(&st); /* again, for eventual weak preserved tables */ | ||
| 486 | cleartablekeys(&st); | 405 | cleartablekeys(&st); |
| 487 | luaC_sweep(L, 0); | 406 | luaC_sweep(L, 0); |
| 488 | checkMbuffer(L); | 407 | checkMbuffer(L); |
| @@ -490,3 +409,11 @@ void luaC_collectgarbage (lua_State *L) { | |||
| 490 | callGCTM(L); | 409 | callGCTM(L); |
| 491 | } | 410 | } |
| 492 | 411 | ||
| 412 | |||
| 413 | void luaC_link (lua_State *L, GCObject *o, int tt) { | ||
| 414 | o->gch.next = G(L)->rootgc; | ||
| 415 | G(L)->rootgc = o; | ||
| 416 | o->gch.marked = 0; | ||
| 417 | o->gch.tt = tt; | ||
| 418 | } | ||
| 419 | |||
