diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-06-12 11:21:44 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-06-12 11:21:44 -0300 |
| commit | 73ec04fcf3e3f7017786fbaf0a83291b22bec5c4 (patch) | |
| tree | b2870a1e832fa3e013b67209592db32b611f8e5f | |
| parent | d13a3fb070fd0ec9ec3b85f88e0fcd914aa666d3 (diff) | |
| download | lua-73ec04fcf3e3f7017786fbaf0a83291b22bec5c4.tar.gz lua-73ec04fcf3e3f7017786fbaf0a83291b22bec5c4.tar.bz2 lua-73ec04fcf3e3f7017786fbaf0a83291b22bec5c4.zip | |
no more 'DEADKEY'. Table traversals do not need to consider dead keys;
if the key is dead, it cannot be given to 'next'. Instead, we now
use a 'table' tag without the collectable bit, which makes it
a unique tag good enough to reserve space.
| -rw-r--r-- | lgc.c | 21 | ||||
| -rw-r--r-- | lobject.h | 22 | ||||
| -rw-r--r-- | lstate.h | 5 | ||||
| -rw-r--r-- | ltable.c | 64 | ||||
| -rw-r--r-- | ltests.c | 3 |
5 files changed, 49 insertions, 66 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lgc.c,v 2.230 2017/06/01 19:16:34 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.231 2017/06/09 16:48:44 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 | */ |
| @@ -75,8 +75,6 @@ | |||
| 75 | 75 | ||
| 76 | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) | 76 | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) |
| 77 | 77 | ||
| 78 | #define checkdeadkey(n) lua_assert(!keyisdead(n) || ttisnil(gval(n))) | ||
| 79 | |||
| 80 | 78 | ||
| 81 | #define checkconsistency(obj) \ | 79 | #define checkconsistency(obj) \ |
| 82 | lua_longassert(!iscollectable(obj) || righttt(obj)) | 80 | lua_longassert(!iscollectable(obj) || righttt(obj)) |
| @@ -119,13 +117,11 @@ static lu_mem atomic (lua_State *L); | |||
| 119 | 117 | ||
| 120 | 118 | ||
| 121 | /* | 119 | /* |
| 122 | ** If key is not marked, mark its entry as dead. This allows key to be | 120 | ** If key is not marked, mark its entry as dead. This allows the |
| 123 | ** collected, but keeps its entry in the table. A dead node is needed | 121 | ** collection of the key, but keeps its entry in the table (its removal |
| 124 | ** when Lua looks up for a key (it may be part of a chain) and when | 122 | ** could break a chain). Other places never manipulate dead keys, |
| 125 | ** traversing a weak table (key might be removed from the table during | 123 | ** because its associated nil value is enough to signal that the entry |
| 126 | ** traversal). Other places never manipulate dead keys, because its | 124 | ** is logically empty. |
| 127 | ** associated nil value is enough to signal that the entry is logically | ||
| 128 | ** empty. | ||
| 129 | */ | 125 | */ |
| 130 | static void removeentry (Node *n) { | 126 | static void removeentry (Node *n) { |
| 131 | lua_assert(ttisnil(gval(n))); | 127 | lua_assert(ttisnil(gval(n))); |
| @@ -203,7 +199,7 @@ LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p) { | |||
| 203 | lua_assert(g->gckind != KGC_GEN || isold(p)); | 199 | lua_assert(g->gckind != KGC_GEN || isold(p)); |
| 204 | if (getage(p) == G_OLD1) /* still need to be visited? */ | 200 | if (getage(p) == G_OLD1) /* still need to be visited? */ |
| 205 | linkgclist(p, g->grayagain); /* link it in 'grayagain' */ | 201 | linkgclist(p, g->grayagain); /* link it in 'grayagain' */ |
| 206 | else | 202 | else |
| 207 | linkgclist(p, g->protogray); /* link it in 'protogray' */ | 203 | linkgclist(p, g->protogray); /* link it in 'protogray' */ |
| 208 | black2gray(p); /* make prototype gray (to avoid other barriers) */ | 204 | black2gray(p); /* make prototype gray (to avoid other barriers) */ |
| 209 | } | 205 | } |
| @@ -391,7 +387,6 @@ static void traverseweakvalue (global_State *g, Table *h) { | |||
| 391 | worth traversing it now just to check) */ | 387 | worth traversing it now just to check) */ |
| 392 | int hasclears = (h->sizearray > 0); | 388 | int hasclears = (h->sizearray > 0); |
| 393 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | 389 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ |
| 394 | checkdeadkey(n); | ||
| 395 | if (ttisnil(gval(n))) /* entry is empty? */ | 390 | if (ttisnil(gval(n))) /* entry is empty? */ |
| 396 | removeentry(n); /* remove it */ | 391 | removeentry(n); /* remove it */ |
| 397 | else { | 392 | else { |
| @@ -434,7 +429,6 @@ static int traverseephemeron (global_State *g, Table *h) { | |||
| 434 | } | 429 | } |
| 435 | /* traverse hash part */ | 430 | /* traverse hash part */ |
| 436 | for (n = gnode(h, 0); n < limit; n++) { | 431 | for (n = gnode(h, 0); n < limit; n++) { |
| 437 | checkdeadkey(n); | ||
| 438 | if (ttisnil(gval(n))) /* entry is empty? */ | 432 | if (ttisnil(gval(n))) /* entry is empty? */ |
| 439 | removeentry(n); /* remove it */ | 433 | removeentry(n); /* remove it */ |
| 440 | else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ | 434 | else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ |
| @@ -468,7 +462,6 @@ static void traversestrongtable (global_State *g, Table *h) { | |||
| 468 | for (i = 0; i < h->sizearray; i++) /* traverse array part */ | 462 | for (i = 0; i < h->sizearray; i++) /* traverse array part */ |
| 469 | markvalue(g, &h->array[i]); | 463 | markvalue(g, &h->array[i]); |
| 470 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | 464 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ |
| 471 | checkdeadkey(n); | ||
| 472 | if (ttisnil(gval(n))) /* entry is empty? */ | 465 | if (ttisnil(gval(n))) /* entry is empty? */ |
| 473 | removeentry(n); /* remove it */ | 466 | removeentry(n); /* remove it */ |
| 474 | else { | 467 | else { |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lobject.h,v 2.121 2017/06/01 20:24:05 roberto Exp roberto $ | 2 | ** $Id: lobject.h,v 2.122 2017/06/09 16:48:44 roberto Exp roberto $ |
| 3 | ** Type definitions for Lua objects | 3 | ** Type definitions for Lua objects |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -21,10 +21,9 @@ | |||
| 21 | */ | 21 | */ |
| 22 | #define LUA_TUPVAL LUA_NUMTAGS /* upvalues */ | 22 | #define LUA_TUPVAL LUA_NUMTAGS /* upvalues */ |
| 23 | #define LUA_TPROTO (LUA_NUMTAGS+1) /* function prototypes */ | 23 | #define LUA_TPROTO (LUA_NUMTAGS+1) /* function prototypes */ |
| 24 | #define LUA_TDEADKEY (LUA_NUMTAGS+2) /* removed keys in tables */ | ||
| 25 | 24 | ||
| 26 | /* | 25 | /* |
| 27 | ** number of all possible tags (including LUA_TNONE but excluding DEADKEY) | 26 | ** number of all possible tags (including LUA_TNONE) |
| 28 | */ | 27 | */ |
| 29 | #define LUA_TOTALTAGS (LUA_TPROTO + 2) | 28 | #define LUA_TOTALTAGS (LUA_TPROTO + 2) |
| 30 | 29 | ||
| @@ -559,14 +558,7 @@ typedef struct Table { | |||
| 559 | #define keyisshrstr(node) (keytt(node) == ctb(LUA_TSHRSTR)) | 558 | #define keyisshrstr(node) (keytt(node) == ctb(LUA_TSHRSTR)) |
| 560 | #define keystrval(node) (gco2ts(keyval(node).gc)) | 559 | #define keystrval(node) (gco2ts(keyval(node).gc)) |
| 561 | 560 | ||
| 562 | #define keyisdead(node) (keytt(node) == LUA_TDEADKEY) | ||
| 563 | |||
| 564 | #define setnilkey(node) (keytt(node) = LUA_TNIL) | 561 | #define setnilkey(node) (keytt(node) = LUA_TNIL) |
| 565 | #define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) | ||
| 566 | |||
| 567 | /* a dead value may get the 'gc' field, but cannot access its contents */ | ||
| 568 | #define deadkey(n) \ | ||
| 569 | check_exp(keytt(n) == LUA_TDEADKEY, cast(void *, keyval(n).gc)) | ||
| 570 | 562 | ||
| 571 | #define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) | 563 | #define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) |
| 572 | 564 | ||
| @@ -574,6 +566,16 @@ typedef struct Table { | |||
| 574 | #define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) | 566 | #define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) |
| 575 | 567 | ||
| 576 | 568 | ||
| 569 | /* | ||
| 570 | ** Use a "nil table" to mark dead keys in a table. Those keys serve | ||
| 571 | ** only to keep space for removed entries, which may still be part of | ||
| 572 | ** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE | ||
| 573 | ** set, so these values are considered not collectable and are different | ||
| 574 | ** from any valid value. | ||
| 575 | */ | ||
| 576 | #define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) | ||
| 577 | |||
| 578 | |||
| 577 | 579 | ||
| 578 | /* | 580 | /* |
| 579 | ** 'module' operation for hashing (size is always a power of 2) | 581 | ** 'module' operation for hashing (size is always a power of 2) |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstate.h,v 2.141 2017/05/13 13:54:47 roberto Exp roberto $ | 2 | ** $Id: lstate.h,v 2.142 2017/05/26 19:14:29 roberto Exp roberto $ |
| 3 | ** Global State | 3 | ** Global State |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -247,8 +247,7 @@ union GCUnion { | |||
| 247 | 247 | ||
| 248 | 248 | ||
| 249 | /* macro to convert a Lua object into a GCObject */ | 249 | /* macro to convert a Lua object into a GCObject */ |
| 250 | #define obj2gco(v) \ | 250 | #define obj2gco(v) (&(cast_u(v)->gc)) |
| 251 | check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc))) | ||
| 252 | 251 | ||
| 253 | 252 | ||
| 254 | /* actual number of total bytes allocated */ | 253 | /* actual number of total bytes allocated */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: ltable.c,v 2.122 2017/05/19 12:57:10 roberto Exp roberto $ | 2 | ** $Id: ltable.c,v 2.123 2017/06/09 16:48:44 roberto Exp roberto $ |
| 3 | ** Lua tables (hash) | 3 | ** Lua tables (hash) |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -176,6 +176,25 @@ static int equalkey (const TValue *k1, const Node *n2) { | |||
| 176 | 176 | ||
| 177 | 177 | ||
| 178 | /* | 178 | /* |
| 179 | ** "Generic" get version. (Not that generic: not valid for integers, | ||
| 180 | ** which may be in array part, nor for floats with integral values.) | ||
| 181 | */ | ||
| 182 | static const TValue *getgeneric (Table *t, const TValue *key) { | ||
| 183 | Node *n = mainpositionTV(t, key); | ||
| 184 | for (;;) { /* check whether 'key' is somewhere in the chain */ | ||
| 185 | if (equalkey(key, n)) | ||
| 186 | return gval(n); /* that's it */ | ||
| 187 | else { | ||
| 188 | int nx = gnext(n); | ||
| 189 | if (nx == 0) | ||
| 190 | return luaO_nilobject; /* not found */ | ||
| 191 | n += nx; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | /* | ||
| 179 | ** returns the index for 'k' if 'k' is an appropriate key to live in | 198 | ** returns the index for 'k' if 'k' is an appropriate key to live in |
| 180 | ** the array part of a table, 0 otherwise. | 199 | ** the array part of a table, 0 otherwise. |
| 181 | */ | 200 | */ |
| @@ -199,22 +218,12 @@ static unsigned int findindex (lua_State *L, Table *t, StkId key) { | |||
| 199 | if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */ | 218 | if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */ |
| 200 | return i; /* yes; that's the index */ | 219 | return i; /* yes; that's the index */ |
| 201 | else { | 220 | else { |
| 202 | int nx; | 221 | const TValue *n = getgeneric(t, key); |
| 203 | Node *n = mainpositionTV(t, key); | 222 | if (n == luaO_nilobject) |
| 204 | for (;;) { /* check whether 'key' is somewhere in the chain */ | 223 | luaG_runerror(L, "invalid key to 'next'"); /* key not found */ |
| 205 | /* key may be dead already, but it is ok to use it in 'next' */ | 224 | i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ |
| 206 | if (equalkey(key, n) || | 225 | /* hash elements are numbered after array ones */ |
| 207 | (keyisdead(n) && iscollectable(key) && | 226 | return (i + 1) + t->sizearray; |
| 208 | deadkey(n) == gcvalue(key))) { | ||
| 209 | i = cast_int(n - gnode(t, 0)); /* key index in hash table */ | ||
| 210 | /* hash elements are numbered after array ones */ | ||
| 211 | return (i + 1) + t->sizearray; | ||
| 212 | } | ||
| 213 | nx = gnext(n); | ||
| 214 | if (nx == 0) | ||
| 215 | luaG_runerror(L, "invalid key to 'next'"); /* key not found */ | ||
| 216 | else n += nx; | ||
| 217 | } | ||
| 218 | } | 227 | } |
| 219 | } | 228 | } |
| 220 | 229 | ||
| @@ -574,25 +583,6 @@ const TValue *luaH_getshortstr (Table *t, TString *key) { | |||
| 574 | } | 583 | } |
| 575 | 584 | ||
| 576 | 585 | ||
| 577 | /* | ||
| 578 | ** "Generic" get version. (Not that generic: not valid for integers, | ||
| 579 | ** which may be in array part, nor for floats with integral values.) | ||
| 580 | */ | ||
| 581 | static const TValue *getgeneric (Table *t, const TValue *key) { | ||
| 582 | Node *n = mainpositionTV(t, key); | ||
| 583 | for (;;) { /* check whether 'key' is somewhere in the chain */ | ||
| 584 | if (equalkey(key, n)) | ||
| 585 | return gval(n); /* that's it */ | ||
| 586 | else { | ||
| 587 | int nx = gnext(n); | ||
| 588 | if (nx == 0) | ||
| 589 | return luaO_nilobject; /* not found */ | ||
| 590 | n += nx; | ||
| 591 | } | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | |||
| 596 | const TValue *luaH_getstr (Table *t, TString *key) { | 586 | const TValue *luaH_getstr (Table *t, TString *key) { |
| 597 | if (key->tt == LUA_TSHRSTR) | 587 | if (key->tt == LUA_TSHRSTR) |
| 598 | return luaH_getshortstr(t, key); | 588 | return luaH_getshortstr(t, key); |
| @@ -662,7 +652,7 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { | |||
| 662 | ** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a | 652 | ** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a |
| 663 | ** boundary. ('j + 1' cannot be a present integer key because it is | 653 | ** boundary. ('j + 1' cannot be a present integer key because it is |
| 664 | ** not a valid integer in Lua.) | 654 | ** not a valid integer in Lua.) |
| 665 | */ | 655 | */ |
| 666 | static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { | 656 | static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { |
| 667 | lua_Unsigned i; | 657 | lua_Unsigned i; |
| 668 | if (j == 0) j++; /* the caller ensures 'j + 1' is present */ | 658 | if (j == 0) j++; /* the caller ensures 'j + 1' is present */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: ltests.c,v 2.218 2017/05/31 18:54:58 roberto Exp roberto $ | 2 | ** $Id: ltests.c,v 2.219 2017/06/09 16:48:44 roberto Exp roberto $ |
| 3 | ** Internal Module for Debugging of the Lua Implementation | 3 | ** Internal Module for Debugging of the Lua Implementation |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -449,7 +449,6 @@ static void markgrays (global_State *g) { | |||
| 449 | checkgraylist(g, g->grayagain); | 449 | checkgraylist(g, g->grayagain); |
| 450 | checkgraylist(g, g->weak); | 450 | checkgraylist(g, g->weak); |
| 451 | checkgraylist(g, g->ephemeron); | 451 | checkgraylist(g, g->ephemeron); |
| 452 | checkgraylist(g, g->allweak); | ||
| 453 | checkgraylist(g, g->protogray); | 452 | checkgraylist(g, g->protogray); |
| 454 | } | 453 | } |
| 455 | 454 | ||
