mirror of
https://github.com/golang/go
synced 2024-11-06 05:36:13 -07:00
161874da2a
Each URL was manually verified to ensure it did not serve up incorrect content. Change-Id: I4dc846227af95a73ee9a3074d0c379ff0fa955df Reviewed-on: https://go-review.googlesource.com/115798 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org>
325 lines
8.8 KiB
Go
325 lines
8.8 KiB
Go
// Copyright 2009 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.
|
|
|
|
// Page heap.
|
|
//
|
|
// See malloc.go for the general overview.
|
|
//
|
|
// Large spans are the subject of this file. Spans consisting of less than
|
|
// _MaxMHeapLists are held in lists of like sized spans. Larger spans
|
|
// are held in a treap. See https://en.wikipedia.org/wiki/Treap or
|
|
// https://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview.
|
|
// sema.go also holds an implementation of a treap.
|
|
//
|
|
// Each treapNode holds a single span. The treap is sorted by page size
|
|
// and for spans of the same size a secondary sort based on start address
|
|
// is done.
|
|
// Spans are returned based on a best fit algorithm and for spans of the same
|
|
// size the one at the lowest address is selected.
|
|
//
|
|
// The primary routines are
|
|
// insert: adds a span to the treap
|
|
// remove: removes the span from that treap that best fits the required size
|
|
// removeSpan: which removes a specific span from the treap
|
|
//
|
|
// _mheap.lock must be held when manipulating this data structure.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
//go:notinheap
|
|
type mTreap struct {
|
|
treap *treapNode
|
|
}
|
|
|
|
//go:notinheap
|
|
type treapNode struct {
|
|
right *treapNode // all treapNodes > this treap node
|
|
left *treapNode // all treapNodes < this treap node
|
|
parent *treapNode // direct parent of this node, nil if root
|
|
npagesKey uintptr // number of pages in spanKey, used as primary sort key
|
|
spanKey *mspan // span of size npagesKey, used as secondary sort key
|
|
priority uint32 // random number used by treap algorithm to keep tree probabilistically balanced
|
|
}
|
|
|
|
func (t *treapNode) init() {
|
|
t.right = nil
|
|
t.left = nil
|
|
t.parent = nil
|
|
t.spanKey = nil
|
|
t.npagesKey = 0
|
|
t.priority = 0
|
|
}
|
|
|
|
// isSpanInTreap is handy for debugging. One should hold the heap lock, usually
|
|
// mheap_.lock().
|
|
func (t *treapNode) isSpanInTreap(s *mspan) bool {
|
|
if t == nil {
|
|
return false
|
|
}
|
|
return t.spanKey == s || t.left.isSpanInTreap(s) || t.right.isSpanInTreap(s)
|
|
}
|
|
|
|
// walkTreap is handy for debugging.
|
|
// Starting at some treapnode t, for example the root, do a depth first preorder walk of
|
|
// the tree executing fn at each treap node. One should hold the heap lock, usually
|
|
// mheap_.lock().
|
|
func (t *treapNode) walkTreap(fn func(tn *treapNode)) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
fn(t)
|
|
t.left.walkTreap(fn)
|
|
t.right.walkTreap(fn)
|
|
}
|
|
|
|
// checkTreapNode when used in conjunction with walkTreap can usually detect a
|
|
// poorly formed treap.
|
|
func checkTreapNode(t *treapNode) {
|
|
// lessThan is used to order the treap.
|
|
// npagesKey and npages are the primary keys.
|
|
// spanKey and span are the secondary keys.
|
|
// span == nil (0) will always be lessThan all
|
|
// spans of the same size.
|
|
lessThan := func(npages uintptr, s *mspan) bool {
|
|
if t.npagesKey != npages {
|
|
return t.npagesKey < npages
|
|
}
|
|
// t.npagesKey == npages
|
|
return uintptr(unsafe.Pointer(t.spanKey)) < uintptr(unsafe.Pointer(s))
|
|
}
|
|
|
|
if t == nil {
|
|
return
|
|
}
|
|
if t.spanKey.npages != t.npagesKey || t.spanKey.next != nil {
|
|
println("runtime: checkTreapNode treapNode t=", t, " t.npagesKey=", t.npagesKey,
|
|
"t.spanKey.npages=", t.spanKey.npages)
|
|
throw("why does span.npages and treap.ngagesKey do not match?")
|
|
}
|
|
if t.left != nil && lessThan(t.left.npagesKey, t.left.spanKey) {
|
|
throw("t.lessThan(t.left.npagesKey, t.left.spanKey) is not false")
|
|
}
|
|
if t.right != nil && !lessThan(t.right.npagesKey, t.right.spanKey) {
|
|
throw("!t.lessThan(t.left.npagesKey, t.left.spanKey) is not false")
|
|
}
|
|
}
|
|
|
|
// insert adds span to the large span treap.
|
|
func (root *mTreap) insert(span *mspan) {
|
|
npages := span.npages
|
|
var last *treapNode
|
|
pt := &root.treap
|
|
for t := *pt; t != nil; t = *pt {
|
|
last = t
|
|
if t.npagesKey < npages {
|
|
pt = &t.right
|
|
} else if t.npagesKey > npages {
|
|
pt = &t.left
|
|
} else if uintptr(unsafe.Pointer(t.spanKey)) < uintptr(unsafe.Pointer(span)) {
|
|
// t.npagesKey == npages, so sort on span addresses.
|
|
pt = &t.right
|
|
} else if uintptr(unsafe.Pointer(t.spanKey)) > uintptr(unsafe.Pointer(span)) {
|
|
pt = &t.left
|
|
} else {
|
|
throw("inserting span already in treap")
|
|
}
|
|
}
|
|
|
|
// Add t as new leaf in tree of span size and unique addrs.
|
|
// The balanced tree is a treap using priority as the random heap priority.
|
|
// That is, it is a binary tree ordered according to the npagesKey,
|
|
// but then among the space of possible binary trees respecting those
|
|
// npagesKeys, it is kept balanced on average by maintaining a heap ordering
|
|
// on the priority: s.priority <= both s.right.priority and s.right.priority.
|
|
// https://en.wikipedia.org/wiki/Treap
|
|
// https://faculty.washington.edu/aragon/pubs/rst89.pdf
|
|
|
|
t := (*treapNode)(mheap_.treapalloc.alloc())
|
|
t.init()
|
|
t.npagesKey = span.npages
|
|
t.priority = fastrand()
|
|
t.spanKey = span
|
|
t.parent = last
|
|
*pt = t // t now at a leaf.
|
|
// Rotate up into tree according to priority.
|
|
for t.parent != nil && t.parent.priority > t.priority {
|
|
if t != nil && t.spanKey.npages != t.npagesKey {
|
|
println("runtime: insert t=", t, "t.npagesKey=", t.npagesKey)
|
|
println("runtime: t.spanKey=", t.spanKey, "t.spanKey.npages=", t.spanKey.npages)
|
|
throw("span and treap sizes do not match?")
|
|
}
|
|
if t.parent.left == t {
|
|
root.rotateRight(t.parent)
|
|
} else {
|
|
if t.parent.right != t {
|
|
throw("treap insert finds a broken treap")
|
|
}
|
|
root.rotateLeft(t.parent)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (root *mTreap) removeNode(t *treapNode) {
|
|
if t.spanKey.npages != t.npagesKey {
|
|
throw("span and treap node npages do not match")
|
|
}
|
|
|
|
// Rotate t down to be leaf of tree for removal, respecting priorities.
|
|
for t.right != nil || t.left != nil {
|
|
if t.right == nil || t.left != nil && t.left.priority < t.right.priority {
|
|
root.rotateRight(t)
|
|
} else {
|
|
root.rotateLeft(t)
|
|
}
|
|
}
|
|
// Remove t, now a leaf.
|
|
if t.parent != nil {
|
|
if t.parent.left == t {
|
|
t.parent.left = nil
|
|
} else {
|
|
t.parent.right = nil
|
|
}
|
|
} else {
|
|
root.treap = nil
|
|
}
|
|
// Return the found treapNode's span after freeing the treapNode.
|
|
t.spanKey = nil
|
|
t.npagesKey = 0
|
|
mheap_.treapalloc.free(unsafe.Pointer(t))
|
|
}
|
|
|
|
// remove searches for, finds, removes from the treap, and returns the smallest
|
|
// span that can hold npages. If no span has at least npages return nil.
|
|
// This is slightly more complicated than a simple binary tree search
|
|
// since if an exact match is not found the next larger node is
|
|
// returned.
|
|
// If the last node inspected > npagesKey not holding
|
|
// a left node (a smaller npages) is the "best fit" node.
|
|
func (root *mTreap) remove(npages uintptr) *mspan {
|
|
t := root.treap
|
|
for t != nil {
|
|
if t.spanKey == nil {
|
|
throw("treap node with nil spanKey found")
|
|
}
|
|
if t.npagesKey < npages {
|
|
t = t.right
|
|
} else if t.left != nil && t.left.npagesKey >= npages {
|
|
t = t.left
|
|
} else {
|
|
result := t.spanKey
|
|
root.removeNode(t)
|
|
return result
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// removeSpan searches for, finds, deletes span along with
|
|
// the associated treap node. If the span is not in the treap
|
|
// then t will eventually be set to nil and the t.spanKey
|
|
// will throw.
|
|
func (root *mTreap) removeSpan(span *mspan) {
|
|
npages := span.npages
|
|
t := root.treap
|
|
for t.spanKey != span {
|
|
if t.npagesKey < npages {
|
|
t = t.right
|
|
} else if t.npagesKey > npages {
|
|
t = t.left
|
|
} else if uintptr(unsafe.Pointer(t.spanKey)) < uintptr(unsafe.Pointer(span)) {
|
|
t = t.right
|
|
} else if uintptr(unsafe.Pointer(t.spanKey)) > uintptr(unsafe.Pointer(span)) {
|
|
t = t.left
|
|
}
|
|
}
|
|
root.removeNode(t)
|
|
}
|
|
|
|
// scavengetreap visits each node in the treap and scavenges the
|
|
// treapNode's span.
|
|
func scavengetreap(treap *treapNode, now, limit uint64) uintptr {
|
|
if treap == nil {
|
|
return 0
|
|
}
|
|
return scavengeTreapNode(treap, now, limit) +
|
|
scavengetreap(treap.left, now, limit) +
|
|
scavengetreap(treap.right, now, limit)
|
|
}
|
|
|
|
// rotateLeft rotates the tree rooted at node x.
|
|
// turning (x a (y b c)) into (y (x a b) c).
|
|
func (root *mTreap) rotateLeft(x *treapNode) {
|
|
// p -> (x a (y b c))
|
|
p := x.parent
|
|
a, y := x.left, x.right
|
|
b, c := y.left, y.right
|
|
|
|
y.left = x
|
|
x.parent = y
|
|
y.right = c
|
|
if c != nil {
|
|
c.parent = y
|
|
}
|
|
x.left = a
|
|
if a != nil {
|
|
a.parent = x
|
|
}
|
|
x.right = b
|
|
if b != nil {
|
|
b.parent = x
|
|
}
|
|
|
|
y.parent = p
|
|
if p == nil {
|
|
root.treap = y
|
|
} else if p.left == x {
|
|
p.left = y
|
|
} else {
|
|
if p.right != x {
|
|
throw("large span treap rotateLeft")
|
|
}
|
|
p.right = y
|
|
}
|
|
}
|
|
|
|
// rotateRight rotates the tree rooted at node y.
|
|
// turning (y (x a b) c) into (x a (y b c)).
|
|
func (root *mTreap) rotateRight(y *treapNode) {
|
|
// p -> (y (x a b) c)
|
|
p := y.parent
|
|
x, c := y.left, y.right
|
|
a, b := x.left, x.right
|
|
|
|
x.left = a
|
|
if a != nil {
|
|
a.parent = x
|
|
}
|
|
x.right = y
|
|
y.parent = x
|
|
y.left = b
|
|
if b != nil {
|
|
b.parent = y
|
|
}
|
|
y.right = c
|
|
if c != nil {
|
|
c.parent = y
|
|
}
|
|
|
|
x.parent = p
|
|
if p == nil {
|
|
root.treap = x
|
|
} else if p.left == y {
|
|
p.left = x
|
|
} else {
|
|
if p.right != y {
|
|
throw("large span treap rotateRight")
|
|
}
|
|
p.right = x
|
|
}
|
|
}
|