mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: prevent garbage collection during hashmap insertion
Inserting a key-value pair into a hashmap storing keys or values indirectly can cause the garbage collector to find the hashmap in an inconsistent state. Fixes #5074. R=golang-dev, minux.ma, rsc CC=golang-dev https://golang.org/cl/7913043
This commit is contained in:
parent
b79afe1b71
commit
54dffda2b6
3 changed files with 33 additions and 4 deletions
|
|
@ -7,6 +7,7 @@ package runtime_test
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -82,3 +83,17 @@ func TestGcDeepNesting(t *testing.T) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGcHashmapIndirection(t *testing.T) {
|
||||||
|
defer debug.SetGCPercent(debug.SetGCPercent(1))
|
||||||
|
runtime.GC()
|
||||||
|
type T struct {
|
||||||
|
a [256]int
|
||||||
|
}
|
||||||
|
m := make(map[T]T)
|
||||||
|
for i := 0; i < 2000; i++ {
|
||||||
|
var a T
|
||||||
|
a.a[0] = i
|
||||||
|
m[a] = T{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1016,10 +1016,12 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av)
|
||||||
res = nil;
|
res = nil;
|
||||||
hit = hash_insert(t, h, ak, (void**)&res);
|
hit = hash_insert(t, h, ak, (void**)&res);
|
||||||
if(!hit) {
|
if(!hit) {
|
||||||
|
// Need to pass dogc=0 to runtime·mallocgc because the garbage collector
|
||||||
|
// is assuming that all hashmaps are in a consistent state.
|
||||||
if(h->flag & IndirectKey)
|
if(h->flag & IndirectKey)
|
||||||
*(void**)res = runtime·mal(t->key->size);
|
*(void**)res = runtime·mallocgc(t->key->size, 0, 0, 1);
|
||||||
if(h->flag & IndirectVal)
|
if(h->flag & IndirectVal)
|
||||||
*(void**)(res+h->valoff) = runtime·mal(t->elem->size);
|
*(void**)(res+h->valoff) = runtime·mallocgc(t->elem->size, 0, 0, 1);
|
||||||
}
|
}
|
||||||
t->key->alg->copy(t->key->size, hash_keyptr(h, res), ak);
|
t->key->alg->copy(t->key->size, hash_keyptr(h, res), ak);
|
||||||
t->elem->alg->copy(t->elem->size, hash_valptr(h, res), av);
|
t->elem->alg->copy(t->elem->size, hash_valptr(h, res), av);
|
||||||
|
|
|
||||||
|
|
@ -932,14 +932,26 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
||||||
if(!(mapkey_kind & KindNoPointers) || d.indirectkey) {
|
if(!(mapkey_kind & KindNoPointers) || d.indirectkey) {
|
||||||
if(!d.indirectkey)
|
if(!d.indirectkey)
|
||||||
*objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti};
|
*objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti};
|
||||||
else
|
else {
|
||||||
|
if(Debug) {
|
||||||
|
obj = *(void**)d.key_data;
|
||||||
|
if(!(arena_start <= obj && obj < arena_used))
|
||||||
|
runtime·throw("scanblock: inconsistent hashmap");
|
||||||
|
}
|
||||||
*ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti};
|
*ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!(mapval_kind & KindNoPointers) || d.indirectval) {
|
if(!(mapval_kind & KindNoPointers) || d.indirectval) {
|
||||||
if(!d.indirectval)
|
if(!d.indirectval)
|
||||||
*objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti};
|
*objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti};
|
||||||
else
|
else {
|
||||||
|
if(Debug) {
|
||||||
|
obj = *(void**)d.val_data;
|
||||||
|
if(!(arena_start <= obj && obj < arena_used))
|
||||||
|
runtime·throw("scanblock: inconsistent hashmap");
|
||||||
|
}
|
||||||
*ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti};
|
*ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue