Commit e40d6e06 authored by Russ Cox's avatar Russ Cox

runtime: random offset for map iteration

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5285042
parent 304cf4dc
...@@ -4085,7 +4085,8 @@ a single byte in the string. ...@@ -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
......
...@@ -80,16 +80,18 @@ typedef struct Hiter Hiter; ...@@ -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];
}; };
......
...@@ -506,20 +506,27 @@ iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) ...@@ -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);
}
elemsize = it->elemsize;
Again:
e_hash = 0;
sub = &it->subtable_state[it->i]; sub = &it->subtable_state[it->i];
e = sub->e; e = sub->e;
last = sub->last; 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) ...@@ -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) ...@@ -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) ...@@ -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
......
...@@ -82,7 +82,9 @@ struct hash_iter { ...@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment