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  }