1
0
mirror of https://github.com/golang/go synced 2024-11-21 14:34:41 -07:00

runtime: random offset for map iteration

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5285042
This commit is contained in:
Russ Cox 2011-10-17 18:49:02 -04:00
parent 304cf4dc9b
commit e40d6e066a
4 changed files with 63 additions and 19 deletions

View File

@ -4085,7 +4085,8 @@ a single byte in the string.
</li> </li>
<li> <li>
The iteration order over maps is not specified. The iteration order over maps is not specified
and is not guaranteed to be the same from one iteration to the next.
If map entries that have not yet been reached are deleted during iteration, If map entries that have not yet been reached are deleted during iteration,
the corresponding iteration values will not be produced. If map entries are the corresponding iteration values will not be produced. If map entries are
inserted during iteration, the behavior is implementation-dependent, but the inserted during iteration, the behavior is implementation-dependent, but the

View File

@ -80,16 +80,18 @@ typedef struct Hiter Hiter;
struct Hiter struct Hiter
{ {
uchar data[8]; // return val from next uchar data[8]; // return val from next
int32 elemsize; // size of elements in table */ int32 elemsize; // size of elements in table
int32 changes; // number of changes observed last time */ int32 changes; // number of changes observed last time
int32 i; // stack pointer in subtable_state */ int32 i; // stack pointer in subtable_state
uchar last[8]; // last hash value returned */ int32 cycled; // actually a bool but pad for next field, a pointer
uchar h[8]; // the hash table */ uchar last[8]; // last hash value returned
uchar cycle[8]; // the value where we started and will stop
uchar h[8]; // the hash table
struct struct
{ {
uchar sub[8]; // pointer into subtable */ uchar sub[8]; // pointer into subtable
uchar start[8]; // pointer into start of subtable */ uchar start[8]; // pointer into start of subtable
uchar end[8]; // pointer into end of subtable */ uchar end[8]; // pointer into end of subtable
uchar pad[8]; uchar pad[8];
} sub[4]; } sub[4];
}; };

View File

@ -506,20 +506,27 @@ iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used)
static void * static void *
hash_next (struct hash_iter *it) hash_next (struct hash_iter *it)
{ {
int32 elemsize = it->elemsize; int32 elemsize;
struct hash_iter_sub *sub = &it->subtable_state[it->i]; struct hash_iter_sub *sub;
struct hash_entry *e = sub->e; struct hash_entry *e;
struct hash_entry *last = sub->last; struct hash_entry *last;
hash_hash_t e_hash = 0; hash_hash_t e_hash;
if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */
if (~it->last_hash == 0)
return (0);
it->changes = it->h->changes; it->changes = it->h->changes;
it->i = 0; it->i = 0;
iter_restart (it, it->h->st, 0); iter_restart (it, it->h->st, 0);
sub = &it->subtable_state[it->i];
e = sub->e;
last = sub->last;
} }
elemsize = it->elemsize;
Again:
e_hash = 0;
sub = &it->subtable_state[it->i];
e = sub->e;
last = sub->last;
if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) {
struct hash_entry *start = HASH_OFFSET (e, -(elemsize * it->h->max_probes)); struct hash_entry *start = HASH_OFFSET (e, -(elemsize * it->h->max_probes));
struct hash_entry *pe = HASH_OFFSET (e, -elemsize); struct hash_entry *pe = HASH_OFFSET (e, -elemsize);
@ -542,8 +549,20 @@ hash_next (struct hash_iter *it)
} }
if (e > last) { if (e > last) {
if (it->i == 0) { if (it->i == 0) {
it->last_hash = HASH_OFFSET (e, -elemsize)->hash; if(!it->cycled) {
sub->e = e; // Wrap to zero and iterate up until it->cycle.
it->cycled = true;
it->last_hash = 0;
it->subtable_state[0].e = it->h->st->entry;
it->subtable_state[0].start = it->h->st->entry;
it->subtable_state[0].last = it->h->st->last;
goto Again;
}
// Set last_hash to impossible value and
// break it->changes, so that check at top of
// hash_next will be used if we get called again.
it->last_hash = ~(uintptr_t)0;
it->changes--;
return (0); return (0);
} else { } else {
it->i--; it->i--;
@ -552,6 +571,15 @@ hash_next (struct hash_iter *it)
last = sub->last; last = sub->last;
} }
} else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) {
if(it->cycled && e->hash > it->cycle) {
// Already returned this.
// Set last_hash to impossible value and
// break it->changes, so that check at top of
// hash_next will be used if we get called again.
it->last_hash = ~(uintptr_t)0;
it->changes--;
return (0);
}
it->last_hash = e->hash; it->last_hash = e->hash;
sub->e = HASH_OFFSET (e, elemsize); sub->e = HASH_OFFSET (e, elemsize);
return (e->data); return (e->data);
@ -581,6 +609,17 @@ hash_iter_init (Hmap *h, struct hash_iter *it)
it->subtable_state[0].e = h->st->entry; it->subtable_state[0].e = h->st->entry;
it->subtable_state[0].start = h->st->entry; it->subtable_state[0].start = h->st->entry;
it->subtable_state[0].last = h->st->last; it->subtable_state[0].last = h->st->last;
// fastrand1 returns 31 useful bits.
// We don't care about not having a bottom bit but we
// do want top bits.
if(sizeof(void*) == 8)
it->cycle = (uint64)runtime·fastrand1()<<33 | (uint64)runtime·fastrand1()<<2;
else
it->cycle = runtime·fastrand1()<<1;
it->cycled = false;
it->last_hash = it->cycle;
iter_restart(it, it->h->st, 0);
} }
static void static void

View File

@ -82,7 +82,9 @@ struct hash_iter {
int32 elemsize; /* size of elements in table */ int32 elemsize; /* size of elements in table */
int32 changes; /* number of changes observed last time */ int32 changes; /* number of changes observed last time */
int32 i; /* stack pointer in subtable_state */ int32 i; /* stack pointer in subtable_state */
bool cycled; /* have reached the end and wrapped to 0 */
hash_hash_t last_hash; /* last hash value returned */ hash_hash_t last_hash; /* last hash value returned */
hash_hash_t cycle; /* hash value where we started */
struct Hmap *h; /* the hash table */ struct Hmap *h; /* the hash table */
struct hash_iter_sub { struct hash_iter_sub {
struct hash_entry *e; /* pointer into subtable */ struct hash_entry *e; /* pointer into subtable */