github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/go-weave/models/maxtree.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build ignore 6 7 // maxtree is a model for a concurrent max-tree. 8 package main 9 10 import ( 11 "fmt" 12 13 "github.com/aclements/go-misc/go-weave/amb" 14 "github.com/aclements/go-misc/go-weave/weave" 15 ) 16 17 var sched = weave.Scheduler{Strategy: &amb.StrategyRandom{}} 18 19 // DFS doesn't work because there are some infinite schedules from CAS 20 // retries. 21 // 22 //var sched = weave.Scheduler{Strategy: &amb.StrategyDFS{}} 23 24 const Depth = 3 25 const Degree = 2 26 27 type Node struct { 28 Name string 29 30 Parent *Node 31 PSlot int 32 Children [Degree]*Node 33 34 Lock weave.Mutex 35 Vals [Degree + 1]int 36 } 37 38 type State struct { 39 Root *Node 40 } 41 42 func main() { 43 var s State 44 leaves := s.Init() 45 sched.Run(func() { 46 sched.Trace("resetting") 47 s.Reset() 48 sched.Trace("reset") 49 50 for times := 0; times < 2; times++ { 51 var wg weave.WaitGroup 52 for i := 0; i < 2; i++ { 53 i := i 54 wg.Add(1) 55 sched.Go(func() { 56 s.worker(leaves[i]) 57 wg.Done() 58 }) 59 } 60 61 sched.Trace("waiting") 62 wg.Wait() 63 } 64 65 sched.Trace("checking") 66 s.Root.Check() 67 //fmt.Println(s.Root.Vals) 68 }) 69 } 70 71 func (s *State) Init() (leaves []*Node) { 72 var rec func(d int, name string) *Node 73 rec = func(d int, name string) *Node { 74 n := &Node{} 75 n.Name = name 76 if d == 1 { 77 leaves = append(leaves, n) 78 return n 79 } 80 for i := range n.Children { 81 child := rec(d-1, fmt.Sprintf("%s/%d", name, i)) 82 n.Children[i] = child 83 child.Parent = n 84 child.PSlot = i 85 } 86 return n 87 } 88 s.Root = rec(Depth, "root") 89 return 90 } 91 92 func (s *State) Reset() { 93 s.Root.Reset() 94 } 95 96 func (n *Node) Reset() { 97 if n == nil { 98 return 99 } 100 n.Vals = [Degree + 1]int{} 101 for _, c := range n.Children { 102 c.Reset() 103 } 104 } 105 106 func (n *Node) Check() int { 107 if n == nil { 108 return 0 109 } 110 111 for i, c := range n.Children { 112 cmax := c.Check() 113 if n.Vals[i] != cmax { 114 panic(fmt.Sprintf("child max %d != parent slot %d", cmax, n.Vals[i])) 115 } 116 } 117 118 return n.maxNoSched() 119 } 120 121 func (s *State) worker(node *Node) { 122 // Pick a node. 123 // var pick func(n *Node) *Node 124 // pick = func(n *Node) *Node { 125 // if n.Children[0] == nil { 126 // return n 127 // } 128 // idx := sched.Amb(len(n.Children) + 1) 129 // if idx == 0 { 130 // return n 131 // } 132 // return pick(n.Children[idx-1]) 133 // } 134 // node := pick(s.Root) 135 // sched.Trace("picked") 136 // 137 // Not necessary when workers are given different nodes. 138 // node.Lock.Lock() 139 // defer node.Lock.Unlock() 140 141 // Set node's value to 0, 1, or 2 so we can both raise and 142 // lower the max. 143 node.Update(sched.Amb(3)) 144 sched.Trace("updated") 145 } 146 147 func (n *Node) Update(val int) { 148 newMax, changed := n.set(Degree, val) 149 if !changed { 150 return 151 } 152 153 for n.Parent != nil { 154 retry: 155 pMax, pChanged := n.Parent.set(n.PSlot, newMax) 156 if checkMax := n.max(); newMax != checkMax { 157 sched.Tracef("retrying newMax=%d checkMax=%d", newMax, checkMax) 158 newMax = checkMax 159 goto retry 160 } 161 162 if !pChanged { 163 break 164 } 165 166 n, newMax = n.Parent, pMax 167 } 168 } 169 170 func (n *Node) set(slot, val int) (newMax int, changed bool) { 171 sched.Tracef("%s[%d] = %d", n.Name, slot, val) 172 oldMax := n.maxNoSched() 173 n.Vals[slot] = val 174 newMax = n.maxNoSched() 175 sched.Sched() 176 177 return newMax, newMax != oldMax 178 } 179 180 func (n *Node) max() int { 181 max := n.maxNoSched() 182 sched.Sched() 183 return max 184 } 185 186 func (n *Node) maxNoSched() int { 187 m := 0 188 for _, v := range n.Vals { 189 if v > m { 190 m = v 191 } 192 } 193 return m 194 }