| // 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 |
| // http://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 keep tree probablistically 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 |
| // http://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) *mspan { |
| if t.spanKey.npages != t.npagesKey { |
| throw("span and treap node npages do not match") |
| } |
| result := t.spanKey |
| |
| // 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)) |
| return result |
| } |
| |
| // 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 |
| } |
| } |