github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/go-weave/models/markterm.go (about)

     1  // Copyright 2016 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  // markterm is a model for finding when mark termination can start
     8  // before all work is drained in Go 1.7. This model is expected to
     9  // fail.
    10  package main
    11  
    12  import (
    13  	"fmt"
    14  
    15  	"github.com/aclements/go-misc/go-weave/amb"
    16  	"github.com/aclements/go-misc/go-weave/weave"
    17  )
    18  
    19  var sched = weave.Scheduler{Strategy: &amb.StrategyRandom{}}
    20  
    21  type State struct {
    22  	workers int
    23  	grey    int
    24  	done    bool
    25  }
    26  
    27  func main() {
    28  	sched.Run(func() {
    29  		var s State
    30  		s.grey = 3
    31  
    32  		for i := 0; i < 2; i++ {
    33  			sched.Go(s.worker)
    34  		}
    35  	})
    36  }
    37  
    38  func (s *State) worker() {
    39  	// TODO: This has a liveness problem: if a worker takes the
    40  	// last work and then doesn't get scheduled again, the other
    41  	// worker will spin. Curiously, duplicate state detection
    42  	// would cut off that path, which means if we see a duplicate
    43  	// with an earlier state in the same path (versus a different
    44  	// path), it's a livelock and perhaps should be reported.
    45  	for {
    46  		s.workers++
    47  		sched.Tracef("s.workers++ (%d)", s.workers)
    48  		sched.Sched()
    49  
    50  		s.check()
    51  
    52  		switch {
    53  		case s.grey <= 0:
    54  			// Do nothing
    55  		case s.grey == 1:
    56  			// Pull one pointer, put none.
    57  			s.grey--
    58  			sched.Tracef("s.grey-- (%d)", s.grey)
    59  			sched.Sched()
    60  		default:
    61  			// Remove two pointers, then add one to simulate
    62  			// pulling a buffer off full and then putting one back
    63  			// one full.
    64  			s.grey -= 2
    65  			sched.Tracef("s.grey -= 2 (%d)", s.grey)
    66  			sched.Sched()
    67  			s.grey++
    68  			sched.Tracef("s.grey++ (%d)", s.grey)
    69  			s.check()
    70  			sched.Sched()
    71  		}
    72  
    73  		var grey int
    74  		if true { // Read full list ("grey") before dec(&workers)
    75  			grey = s.grey
    76  			sched.Tracef("grey := s.grey (%d)", s.grey)
    77  			sched.Sched()
    78  		}
    79  
    80  		s.workers--
    81  		n := s.workers
    82  		sched.Tracef("s.workers-- (%d)", s.workers)
    83  		sched.Sched()
    84  
    85  		if false {
    86  			grey = s.grey
    87  			sched.Tracef("grey := s.grey (%d)", s.grey)
    88  			sched.Sched()
    89  		}
    90  
    91  		if n == 0 && grey == 0 {
    92  			s.done = true
    93  			sched.Trace("s.done = true")
    94  			sched.Sched()
    95  			s.check()
    96  			break
    97  		}
    98  	}
    99  	sched.Trace("exit")
   100  }
   101  
   102  func (s *State) check() {
   103  	if s.done && s.grey > 0 {
   104  		panic(fmt.Sprintf("done, but grey==%d", s.grey))
   105  	}
   106  }