2019-02-09 00:13:37 +00:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package runtime_test
|
|
|
|
|
|
|
|
|
|
import (
|
2019-04-29 20:42:27 +00:00
|
|
|
"fmt"
|
2019-02-09 00:13:37 +00:00
|
|
|
"runtime"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2019-04-29 20:42:27 +00:00
|
|
|
var spanDesc = map[uintptr]struct {
|
|
|
|
|
pages uintptr
|
|
|
|
|
scav bool
|
|
|
|
|
}{
|
|
|
|
|
0xc0000000: {2, false},
|
|
|
|
|
0xc0006000: {1, false},
|
|
|
|
|
0xc0010000: {8, false},
|
|
|
|
|
0xc0022000: {7, false},
|
|
|
|
|
0xc0034000: {4, true},
|
|
|
|
|
0xc0040000: {5, false},
|
|
|
|
|
0xc0050000: {5, true},
|
|
|
|
|
0xc0060000: {5000, false},
|
2019-02-09 00:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrap the Treap one more time because go:notinheap doesn't
|
|
|
|
|
// actually follow a structure across package boundaries.
|
|
|
|
|
//
|
|
|
|
|
//go:notinheap
|
|
|
|
|
type treap struct {
|
|
|
|
|
runtime.Treap
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-29 20:42:27 +00:00
|
|
|
func maskMatchName(mask, match runtime.TreapIterType) string {
|
|
|
|
|
return fmt.Sprintf("%0*b-%0*b", runtime.TreapIterBits, uint8(mask), runtime.TreapIterBits, uint8(match))
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-09 00:13:37 +00:00
|
|
|
// This test ensures that the treap implementation in the runtime
|
|
|
|
|
// maintains all stated invariants after different sequences of
|
|
|
|
|
// insert, removeSpan, find, and erase. Invariants specific to the
|
|
|
|
|
// treap data structure are checked implicitly: after each mutating
|
|
|
|
|
// operation, treap-related invariants are checked for the entire
|
|
|
|
|
// treap.
|
|
|
|
|
func TestTreap(t *testing.T) {
|
|
|
|
|
// Set up a bunch of spans allocated into mheap_.
|
2019-04-29 20:42:27 +00:00
|
|
|
// Also, derive a set of typeCounts of each type of span
|
|
|
|
|
// according to runtime.TreapIterType so we can verify against
|
|
|
|
|
// them later.
|
2019-02-09 00:13:37 +00:00
|
|
|
spans := make([]runtime.Span, 0, len(spanDesc))
|
2019-04-29 20:42:27 +00:00
|
|
|
typeCounts := [1 << runtime.TreapIterBits][1 << runtime.TreapIterBits]int{}
|
|
|
|
|
for base, de := range spanDesc {
|
|
|
|
|
s := runtime.AllocSpan(base, de.pages, de.scav)
|
2019-02-09 00:13:37 +00:00
|
|
|
defer s.Free()
|
|
|
|
|
spans = append(spans, s)
|
2019-04-29 20:42:27 +00:00
|
|
|
|
|
|
|
|
for i := runtime.TreapIterType(0); i < 1<<runtime.TreapIterBits; i++ {
|
|
|
|
|
for j := runtime.TreapIterType(0); j < 1<<runtime.TreapIterBits; j++ {
|
|
|
|
|
if s.MatchesIter(i, j) {
|
|
|
|
|
typeCounts[i][j]++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-02-09 00:13:37 +00:00
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
t.Run("TypeCountsSanity", func(t *testing.T) {
|
|
|
|
|
// Just sanity check type counts for a few values.
|
|
|
|
|
check := func(mask, match runtime.TreapIterType, count int) {
|
|
|
|
|
tc := typeCounts[mask][match]
|
|
|
|
|
if tc != count {
|
|
|
|
|
name := maskMatchName(mask, match)
|
|
|
|
|
t.Fatalf("failed a sanity check for mask/match %s counts: got %d, wanted %d", name, tc, count)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
check(0, 0, len(spanDesc))
|
|
|
|
|
check(runtime.TreapIterScav, 0, 6)
|
|
|
|
|
check(runtime.TreapIterScav, runtime.TreapIterScav, 2)
|
|
|
|
|
})
|
2019-02-09 00:13:37 +00:00
|
|
|
t.Run("Insert", func(t *testing.T) {
|
|
|
|
|
tr := treap{}
|
|
|
|
|
// Test just a very basic insert/remove for sanity.
|
|
|
|
|
tr.Insert(spans[0])
|
|
|
|
|
tr.RemoveSpan(spans[0])
|
|
|
|
|
})
|
|
|
|
|
t.Run("FindTrivial", func(t *testing.T) {
|
|
|
|
|
tr := treap{}
|
|
|
|
|
// Test just a very basic find operation for sanity.
|
|
|
|
|
tr.Insert(spans[0])
|
|
|
|
|
i := tr.Find(1)
|
|
|
|
|
if i.Span() != spans[0] {
|
|
|
|
|
t.Fatal("found unknown span in treap")
|
|
|
|
|
}
|
|
|
|
|
tr.RemoveSpan(spans[0])
|
|
|
|
|
})
|
2019-02-05 00:06:55 +00:00
|
|
|
t.Run("FindFirstFit", func(t *testing.T) {
|
2019-02-09 00:13:37 +00:00
|
|
|
// Run this 10 times, recreating the treap each time.
|
|
|
|
|
// Because of the non-deterministic structure of a treap,
|
|
|
|
|
// we'll be able to test different structures this way.
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2019-02-05 00:06:55 +00:00
|
|
|
tr := runtime.Treap{}
|
2019-02-09 00:13:37 +00:00
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
|
|
|
|
i := tr.Find(5)
|
2019-02-05 00:06:55 +00:00
|
|
|
if i.Span().Base() != 0xc0010000 {
|
|
|
|
|
t.Fatalf("expected span at lowest address which could fit 5 pages, instead found span at %x", i.Span().Base())
|
2019-02-09 00:13:37 +00:00
|
|
|
}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
t.Run("Iterate", func(t *testing.T) {
|
2019-04-29 20:42:27 +00:00
|
|
|
for mask := runtime.TreapIterType(0); mask < 1<<runtime.TreapIterBits; mask++ {
|
|
|
|
|
for match := runtime.TreapIterType(0); match < 1<<runtime.TreapIterBits; match++ {
|
|
|
|
|
iterName := maskMatchName(mask, match)
|
|
|
|
|
t.Run(iterName, func(t *testing.T) {
|
|
|
|
|
t.Run("StartToEnd", func(t *testing.T) {
|
|
|
|
|
// Ensure progressing an iterator actually goes over the whole treap
|
|
|
|
|
// from the start and that it iterates over the elements in order.
|
|
|
|
|
// Furthermore, ensure that it only iterates over the relevant parts
|
|
|
|
|
// of the treap.
|
|
|
|
|
// Finally, ensures that Start returns a valid iterator.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
|
|
|
|
nspans := 0
|
|
|
|
|
lastBase := uintptr(0)
|
|
|
|
|
for i := tr.Start(mask, match); i.Valid(); i = i.Next() {
|
|
|
|
|
nspans++
|
|
|
|
|
if lastBase > i.Span().Base() {
|
|
|
|
|
t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base())
|
|
|
|
|
}
|
|
|
|
|
lastBase = i.Span().Base()
|
|
|
|
|
if !i.Span().MatchesIter(mask, match) {
|
|
|
|
|
t.Fatalf("found non-matching span while iteration over mask/match %s: base %x", iterName, i.Span().Base())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if nspans != typeCounts[mask][match] {
|
|
|
|
|
t.Fatal("failed to iterate forwards over full treap")
|
|
|
|
|
}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
t.Run("EndToStart", func(t *testing.T) {
|
|
|
|
|
// See StartToEnd tests.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
|
|
|
|
nspans := 0
|
|
|
|
|
lastBase := ^uintptr(0)
|
|
|
|
|
for i := tr.End(mask, match); i.Valid(); i = i.Prev() {
|
|
|
|
|
nspans++
|
|
|
|
|
if lastBase < i.Span().Base() {
|
|
|
|
|
t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base())
|
|
|
|
|
}
|
|
|
|
|
lastBase = i.Span().Base()
|
|
|
|
|
if !i.Span().MatchesIter(mask, match) {
|
|
|
|
|
t.Fatalf("found non-matching span while iteration over mask/match %s: base %x", iterName, i.Span().Base())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if nspans != typeCounts[mask][match] {
|
|
|
|
|
t.Fatal("failed to iterate backwards over full treap")
|
|
|
|
|
}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
2019-02-09 00:13:37 +00:00
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
}
|
2019-02-09 00:13:37 +00:00
|
|
|
t.Run("Prev", func(t *testing.T) {
|
|
|
|
|
// Test the iterator invariant that i.prev().next() == i.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
i := tr.Start(0, 0).Next().Next()
|
2019-02-09 00:13:37 +00:00
|
|
|
p := i.Prev()
|
|
|
|
|
if !p.Valid() {
|
|
|
|
|
t.Fatal("i.prev() is invalid")
|
|
|
|
|
}
|
|
|
|
|
if p.Next().Span() != i.Span() {
|
|
|
|
|
t.Fatal("i.prev().next() != i")
|
|
|
|
|
}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
t.Run("Next", func(t *testing.T) {
|
|
|
|
|
// Test the iterator invariant that i.next().prev() == i.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
i := tr.Start(0, 0).Next().Next()
|
2019-02-09 00:13:37 +00:00
|
|
|
n := i.Next()
|
|
|
|
|
if !n.Valid() {
|
|
|
|
|
t.Fatal("i.next() is invalid")
|
|
|
|
|
}
|
|
|
|
|
if n.Prev().Span() != i.Span() {
|
|
|
|
|
t.Fatal("i.next().prev() != i")
|
|
|
|
|
}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
t.Run("EraseOne", func(t *testing.T) {
|
|
|
|
|
// Test that erasing one iterator correctly retains
|
|
|
|
|
// all relationships between elements.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
i := tr.Start(0, 0).Next().Next().Next()
|
2019-02-09 00:13:37 +00:00
|
|
|
s := i.Span()
|
|
|
|
|
n := i.Next()
|
|
|
|
|
p := i.Prev()
|
|
|
|
|
tr.Erase(i)
|
|
|
|
|
if n.Prev().Span() != p.Span() {
|
|
|
|
|
t.Fatal("p, n := i.Prev(), i.Next(); n.prev() != p after i was erased")
|
|
|
|
|
}
|
|
|
|
|
if p.Next().Span() != n.Span() {
|
|
|
|
|
t.Fatal("p, n := i.Prev(), i.Next(); p.next() != n after i was erased")
|
|
|
|
|
}
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.RemoveSpan(s)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
t.Run("EraseAll", func(t *testing.T) {
|
|
|
|
|
// Test that erasing iterators actually removes nodes from the treap.
|
|
|
|
|
tr := treap{}
|
|
|
|
|
for _, s := range spans {
|
|
|
|
|
tr.Insert(s)
|
|
|
|
|
}
|
2019-04-29 20:42:27 +00:00
|
|
|
for i := tr.Start(0, 0); i.Valid(); {
|
2019-02-09 00:13:37 +00:00
|
|
|
n := i.Next()
|
|
|
|
|
tr.Erase(i)
|
|
|
|
|
i = n
|
|
|
|
|
}
|
|
|
|
|
if size := tr.Size(); size != 0 {
|
|
|
|
|
t.Fatalf("should have emptied out treap, %d spans left", size)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|