mirror of
https://github.com/golang/go
synced 2024-11-15 04:40:28 -07:00
internal/runtime/maps: cleanup seed usage
Keep only a single seed; initialize it; and reset it when the map is empty. For #54766. Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest-swissmap Change-Id: Icc231f70957337a2d0dcd9c7daf9bd3cb4354d71 Reviewed-on: https://go-review.googlesource.com/c/go/+/616466 Auto-Submit: Michael Pratt <mpratt@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
f782e16162
commit
d95b7980aa
@ -104,8 +104,6 @@ func swissTableType() *types.Type {
|
|||||||
// localDepth uint8
|
// localDepth uint8
|
||||||
// // N.B Padding
|
// // N.B Padding
|
||||||
//
|
//
|
||||||
// seed uintptr
|
|
||||||
//
|
|
||||||
// index int
|
// index int
|
||||||
//
|
//
|
||||||
// // From groups.
|
// // From groups.
|
||||||
@ -119,7 +117,6 @@ func swissTableType() *types.Type {
|
|||||||
makefield("capacity", types.Types[types.TUINT16]),
|
makefield("capacity", types.Types[types.TUINT16]),
|
||||||
makefield("growthLeft", types.Types[types.TUINT16]),
|
makefield("growthLeft", types.Types[types.TUINT16]),
|
||||||
makefield("localDepth", types.Types[types.TUINT8]),
|
makefield("localDepth", types.Types[types.TUINT8]),
|
||||||
makefield("seed", types.Types[types.TUINTPTR]),
|
|
||||||
makefield("index", types.Types[types.TINT]),
|
makefield("index", types.Types[types.TINT]),
|
||||||
makefield("groups_data", types.Types[types.TUNSAFEPTR]),
|
makefield("groups_data", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("groups_lengthMask", types.Types[types.TUINT64]),
|
makefield("groups_lengthMask", types.Types[types.TUINT64]),
|
||||||
@ -134,9 +131,9 @@ func swissTableType() *types.Type {
|
|||||||
table.SetUnderlying(types.NewStruct(fields))
|
table.SetUnderlying(types.NewStruct(fields))
|
||||||
types.CalcSize(table)
|
types.CalcSize(table)
|
||||||
|
|
||||||
// The size of table should be 48 bytes on 64 bit
|
// The size of table should be 40 bytes on 64 bit
|
||||||
// and 36 bytes on 32 bit platforms.
|
// and 32 bytes on 32 bit platforms.
|
||||||
if size := int64(3*2 + 2*1 /* one extra for padding */ + 2*8 + 3*types.PtrSize); table.Size() != size {
|
if size := int64(3*2 + 2*1 /* one extra for padding */ + 2*8 + 2*types.PtrSize); table.Size() != size {
|
||||||
base.Fatalf("internal/runtime/maps.table size not correct: got %d, want %d", table.Size(), size)
|
base.Fatalf("internal/runtime/maps.table size not correct: got %d, want %d", table.Size(), size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,6 @@ type Map struct {
|
|||||||
used uint64
|
used uint64
|
||||||
|
|
||||||
// seed is the hash seed, computed as a unique random number per map.
|
// seed is the hash seed, computed as a unique random number per map.
|
||||||
// TODO(prattmic): Populate this on table initialization.
|
|
||||||
seed uintptr
|
seed uintptr
|
||||||
|
|
||||||
// The directory of tables.
|
// The directory of tables.
|
||||||
@ -293,10 +292,7 @@ func NewMap(mt *abi.SwissMapType, hint, maxAlloc uintptr) *Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m := &Map{
|
m := &Map{
|
||||||
//TODO
|
seed: uintptr(rand()),
|
||||||
//seed: uintptr(rand()),
|
|
||||||
|
|
||||||
//directory: make([]*table, dirSize),
|
|
||||||
|
|
||||||
globalDepth: globalDepth,
|
globalDepth: globalDepth,
|
||||||
globalShift: depthToShift(globalDepth),
|
globalShift: depthToShift(globalDepth),
|
||||||
@ -654,6 +650,13 @@ func (m *Map) Delete(typ *abi.SwissMapType, key unsafe.Pointer) {
|
|||||||
m.directoryAt(idx).Delete(typ, m, key)
|
m.directoryAt(idx).Delete(typ, m, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.used == 0 {
|
||||||
|
// Reset the hash seed to make it more difficult for attackers
|
||||||
|
// to repeatedly trigger hash collisions. See
|
||||||
|
// https://go.dev/issue/25237.
|
||||||
|
m.seed = uintptr(rand())
|
||||||
|
}
|
||||||
|
|
||||||
if m.writing == 0 {
|
if m.writing == 0 {
|
||||||
fatal("concurrent map writes")
|
fatal("concurrent map writes")
|
||||||
}
|
}
|
||||||
@ -735,6 +738,10 @@ func (m *Map) Clear(typ *abi.SwissMapType) {
|
|||||||
// TODO: shrink directory?
|
// TODO: shrink directory?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the hash seed to make it more difficult for attackers to
|
||||||
|
// repeatedly trigger hash collisions. See https://go.dev/issue/25237.
|
||||||
|
m.seed = uintptr(rand())
|
||||||
|
|
||||||
if m.writing == 0 {
|
if m.writing == 0 {
|
||||||
fatal("concurrent map writes")
|
fatal("concurrent map writes")
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ outer:
|
|||||||
if key == *(*uint32)(slotKey) {
|
if key == *(*uint32)(slotKey) {
|
||||||
slotElem = g.elem(typ, i)
|
slotElem = g.elem(typ, i)
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -297,7 +297,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ outer:
|
|||||||
if key == *(*unsafe.Pointer)(slotKey) {
|
if key == *(*unsafe.Pointer)(slotKey) {
|
||||||
slotElem = g.elem(typ, i)
|
slotElem = g.elem(typ, i)
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -438,7 +438,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ outer:
|
|||||||
if key == *(*uint64)(slotKey) {
|
if key == *(*uint64)(slotKey) {
|
||||||
slotElem = g.elem(typ, i)
|
slotElem = g.elem(typ, i)
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -297,7 +297,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +438,7 @@ outer:
|
|||||||
if key == *(*unsafe.Pointer)(slotKey) {
|
if key == *(*unsafe.Pointer)(slotKey) {
|
||||||
slotElem = g.elem(typ, i)
|
slotElem = g.elem(typ, i)
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -476,7 +476,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ outer:
|
|||||||
*(*string)(slotKey) = key
|
*(*string)(slotKey) = key
|
||||||
slotElem = g.elem(typ, i)
|
slotElem = g.elem(typ, i)
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -304,7 +304,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ outer:
|
|||||||
slotElem = *((*unsafe.Pointer)(slotElem))
|
slotElem = *((*unsafe.Pointer)(slotElem))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -317,7 +317,7 @@ outer:
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +51,6 @@ type table struct {
|
|||||||
// but this table has not yet been split.
|
// but this table has not yet been split.
|
||||||
localDepth uint8
|
localDepth uint8
|
||||||
|
|
||||||
// seed is the hash seed, computed as a unique random number per table.
|
|
||||||
// TODO(prattmic): Populate this on table initialization.
|
|
||||||
seed uintptr
|
|
||||||
|
|
||||||
// Index of this table in the Map directory. This is the index of the
|
// Index of this table in the Map directory. This is the index of the
|
||||||
// _first_ location in the directory. The table may occur in multiple
|
// _first_ location in the directory. The table may occur in multiple
|
||||||
// sequential indicies.
|
// sequential indicies.
|
||||||
@ -148,15 +144,15 @@ func (t *table) Used() uint64 {
|
|||||||
|
|
||||||
// Get performs a lookup of the key that key points to. It returns a pointer to
|
// Get performs a lookup of the key that key points to. It returns a pointer to
|
||||||
// the element, or false if the key doesn't exist.
|
// the element, or false if the key doesn't exist.
|
||||||
func (t *table) Get(typ *abi.SwissMapType, key unsafe.Pointer) (unsafe.Pointer, bool) {
|
func (t *table) Get(typ *abi.SwissMapType, m *Map, key unsafe.Pointer) (unsafe.Pointer, bool) {
|
||||||
// TODO(prattmic): We could avoid hashing in a variety of special
|
// TODO(prattmic): We could avoid hashing in a variety of special
|
||||||
// cases.
|
// cases.
|
||||||
//
|
//
|
||||||
// - One entry maps could just directly compare the single entry
|
// - One entry maps could just directly compare the single entry
|
||||||
// without hashing.
|
// without hashing.
|
||||||
// - String keys could do quick checks of a few bytes before hashing.
|
// - String keys could do quick checks of a few bytes before hashing.
|
||||||
hash := typ.Hasher(key, t.seed)
|
hash := typ.Hasher(key, m.seed)
|
||||||
_, elem, ok := t.getWithKey(typ, hash, key)
|
_, elem, ok := t.getWithKey(typ, hash, key)
|
||||||
return elem, ok
|
return elem, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +295,7 @@ func (t *table) PutSlot(typ *abi.SwissMapType, m *Map, hash uintptr, key unsafe.
|
|||||||
slotElem = *((*unsafe.Pointer)(slotElem))
|
slotElem = *((*unsafe.Pointer)(slotElem))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
return slotElem, true
|
return slotElem, true
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -347,7 +343,7 @@ func (t *table) PutSlot(typ *abi.SwissMapType, m *Map, hash uintptr, key unsafe.
|
|||||||
t.used++
|
t.used++
|
||||||
m.used++
|
m.used++
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
return slotElem, true
|
return slotElem, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +421,7 @@ func (t *table) uncheckedPutSlot(typ *abi.SwissMapType, hash uintptr, key unsafe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) Delete(typ *abi.SwissMapType, m *Map, key unsafe.Pointer) {
|
func (t *table) Delete(typ *abi.SwissMapType, m *Map, key unsafe.Pointer) {
|
||||||
hash := typ.Hasher(key, t.seed)
|
hash := typ.Hasher(key, m.seed)
|
||||||
|
|
||||||
seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
|
seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
|
||||||
for ; ; seq = seq.next() {
|
for ; ; seq = seq.next() {
|
||||||
@ -482,7 +478,7 @@ func (t *table) Delete(typ *abi.SwissMapType, m *Map, key unsafe.Pointer) {
|
|||||||
g.ctrls().set(i, ctrlDeleted)
|
g.ctrls().set(i, ctrlDeleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.checkInvariants(typ)
|
t.checkInvariants(typ, m)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
match = match.removeFirst()
|
match = match.removeFirst()
|
||||||
@ -514,12 +510,6 @@ func (t *table) Clear(typ *abi.SwissMapType) {
|
|||||||
|
|
||||||
t.used = 0
|
t.used = 0
|
||||||
t.resetGrowthLeft()
|
t.resetGrowthLeft()
|
||||||
|
|
||||||
// Reset the hash seed to make it more difficult for attackers to
|
|
||||||
// repeatedly trigger hash collisions. See issue
|
|
||||||
// https://github.com/golang/go/issues/25237.
|
|
||||||
// TODO
|
|
||||||
//t.seed = uintptr(rand())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Iter struct {
|
type Iter struct {
|
||||||
@ -948,7 +938,7 @@ func (t *table) split(typ *abi.SwissMapType, m *Map) {
|
|||||||
elem = *((*unsafe.Pointer)(elem))
|
elem = *((*unsafe.Pointer)(elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := typ.Hasher(key, t.seed)
|
hash := typ.Hasher(key, m.seed)
|
||||||
var newTable *table
|
var newTable *table
|
||||||
if hash&mask == 0 {
|
if hash&mask == 0 {
|
||||||
newTable = left
|
newTable = left
|
||||||
@ -994,7 +984,7 @@ func (t *table) grow(typ *abi.SwissMapType, m *Map, newCapacity uint16) {
|
|||||||
elem = *((*unsafe.Pointer)(elem))
|
elem = *((*unsafe.Pointer)(elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := typ.Hasher(key, t.seed)
|
hash := typ.Hasher(key, m.seed)
|
||||||
|
|
||||||
// TODO(prattmic): For indirect key/elem, this is
|
// TODO(prattmic): For indirect key/elem, this is
|
||||||
// allocating new objects for key/elem. That is
|
// allocating new objects for key/elem. That is
|
||||||
@ -1007,7 +997,7 @@ func (t *table) grow(typ *abi.SwissMapType, m *Map, newCapacity uint16) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newTable.checkInvariants(typ)
|
newTable.checkInvariants(typ, m)
|
||||||
m.replaceTable(newTable)
|
m.replaceTable(newTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
const debugLog = false
|
const debugLog = false
|
||||||
|
|
||||||
func (t *table) checkInvariants(typ *abi.SwissMapType) {
|
func (t *table) checkInvariants(typ *abi.SwissMapType, m *Map) {
|
||||||
if !debugLog {
|
if !debugLog {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -45,12 +45,12 @@ func (t *table) checkInvariants(typ *abi.SwissMapType) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.Get(typ, key); !ok {
|
if _, ok := t.Get(typ, m, key); !ok {
|
||||||
hash := typ.Hasher(key, t.seed)
|
hash := typ.Hasher(key, m.seed)
|
||||||
print("invariant failed: slot(", i, "/", j, "): key ")
|
print("invariant failed: slot(", i, "/", j, "): key ")
|
||||||
dump(key, typ.Key.Size_)
|
dump(key, typ.Key.Size_)
|
||||||
print(" not found [hash=", hash, ", h2=", h2(hash), " h1=", h1(hash), "]\n")
|
print(" not found [hash=", hash, ", h2=", h2(hash), " h1=", h1(hash), "]\n")
|
||||||
t.Print(typ)
|
t.Print(typ, m)
|
||||||
panic("invariant failed: slot: key not found")
|
panic("invariant failed: slot: key not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,32 +59,30 @@ func (t *table) checkInvariants(typ *abi.SwissMapType) {
|
|||||||
|
|
||||||
if used != t.used {
|
if used != t.used {
|
||||||
print("invariant failed: found ", used, " used slots, but used count is ", t.used, "\n")
|
print("invariant failed: found ", used, " used slots, but used count is ", t.used, "\n")
|
||||||
t.Print(typ)
|
t.Print(typ, m)
|
||||||
panic("invariant failed: found mismatched used slot count")
|
panic("invariant failed: found mismatched used slot count")
|
||||||
}
|
}
|
||||||
|
|
||||||
growthLeft := (t.capacity*maxAvgGroupLoad)/abi.SwissMapGroupSlots - t.used - deleted
|
growthLeft := (t.capacity*maxAvgGroupLoad)/abi.SwissMapGroupSlots - t.used - deleted
|
||||||
if growthLeft != t.growthLeft {
|
if growthLeft != t.growthLeft {
|
||||||
print("invariant failed: found ", t.growthLeft, " growthLeft, but expected ", growthLeft, "\n")
|
print("invariant failed: found ", t.growthLeft, " growthLeft, but expected ", growthLeft, "\n")
|
||||||
t.Print(typ)
|
t.Print(typ, m)
|
||||||
panic("invariant failed: found mismatched growthLeft")
|
panic("invariant failed: found mismatched growthLeft")
|
||||||
}
|
}
|
||||||
if deleted != t.tombstones() {
|
if deleted != t.tombstones() {
|
||||||
print("invariant failed: found ", deleted, " tombstones, but expected ", t.tombstones(), "\n")
|
print("invariant failed: found ", deleted, " tombstones, but expected ", t.tombstones(), "\n")
|
||||||
t.Print(typ)
|
t.Print(typ, m)
|
||||||
panic("invariant failed: found mismatched tombstones")
|
panic("invariant failed: found mismatched tombstones")
|
||||||
}
|
}
|
||||||
|
|
||||||
if empty == 0 {
|
if empty == 0 {
|
||||||
print("invariant failed: found no empty slots (violates probe invariant)\n")
|
print("invariant failed: found no empty slots (violates probe invariant)\n")
|
||||||
t.Print(typ)
|
t.Print(typ, m)
|
||||||
panic("invariant failed: found no empty slots (violates probe invariant)")
|
panic("invariant failed: found no empty slots (violates probe invariant)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (t *table) Print(typ *abi.SwissMapType, m *Map) {
|
||||||
func (t *table) Print(typ *abi.SwissMapType) {
|
|
||||||
print(`table{
|
print(`table{
|
||||||
seed: `, t.seed, `
|
|
||||||
index: `, t.index, `
|
index: `, t.index, `
|
||||||
localDepth: `, t.localDepth, `
|
localDepth: `, t.localDepth, `
|
||||||
capacity: `, t.capacity, `
|
capacity: `, t.capacity, `
|
||||||
|
Loading…
Reference in New Issue
Block a user