github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/go-weave/models/issue16083.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  // issue16083 is a model for finding how mark can complete when stack
     8  // scans are still in progress.
     9  //
    10  // XXX Move gcMarkRootCheck to after forEachP to force final workers
    11  // out?
    12  package main
    13  
    14  import (
    15  	"fmt"
    16  
    17  	"github.com/aclements/go-misc/go-weave/amb"
    18  	"github.com/aclements/go-misc/go-weave/weave"
    19  )
    20  
    21  var sched = weave.Scheduler{Strategy: &amb.StrategyRandom{}}
    22  
    23  type State struct {
    24  	workers      weave.AtomicInt32
    25  	markrootNext weave.AtomicInt32
    26  	markrootJobs int32
    27  	scanned      [2]weave.AtomicInt32
    28  
    29  	markDoneSema weave.Mutex
    30  
    31  	done bool
    32  }
    33  
    34  func main() {
    35  	sched.Run(func() {
    36  		var s State
    37  		s.markrootJobs = int32(len(s.scanned))
    38  
    39  		for i := 0; i < 4; i++ {
    40  			sched.Go(s.worker)
    41  		}
    42  	})
    43  }
    44  
    45  func (s *State) worker() {
    46  	// This has a liveness problem, so limit it to 4 iterations.
    47  	for i := 0; i < 3 && !s.done; i++ {
    48  		sched.Trace("s.workers++")
    49  		n := s.workers.Add(+1)
    50  		// XXX This trace appears in the wrong place since Add
    51  		// did a Sched after the modification. Perhaps we
    52  		// should pre-Sched? Or I could put this in an atomic block.
    53  		sched.Tracef(" => %d", n)
    54  
    55  		s.gcDrain()
    56  
    57  		sched.Trace("s.workers--")
    58  		n = s.workers.Add(-1)
    59  		sched.Tracef(" => %d", n)
    60  
    61  		if n == 0 {
    62  			sched.Tracef("s.workers == 0")
    63  			if !s.gcMarkWorkAvailable() {
    64  				sched.Tracef("!gcMarkWorkAvailable()")
    65  				s.gcMarkDone()
    66  			}
    67  		}
    68  	}
    69  	sched.Trace("exit")
    70  }
    71  
    72  func (s *State) gcDrain() {
    73  	job := s.markrootNext.Add(1) - 1
    74  	if job < s.markrootJobs {
    75  		sched.Tracef("scanning %d", job)
    76  		s.scanned[job].Store(1)
    77  	}
    78  }
    79  
    80  func (s *State) gcMarkWorkAvailable() bool {
    81  	return s.markrootNext.Load() < s.markrootJobs
    82  }
    83  
    84  func (s *State) gcMarkDone() {
    85  	s.markDoneSema.Lock()
    86  	defer s.markDoneSema.Unlock()
    87  
    88  	if !(s.workers.Load() == 0 && !s.gcMarkWorkAvailable()) {
    89  		sched.Tracef("gcMarkDone retry")
    90  		return
    91  	}
    92  
    93  	s.gcMarkRootCheck()
    94  
    95  	s.done = true
    96  }
    97  
    98  func (s *State) gcMarkRootCheck() {
    99  	for i := range s.scanned {
   100  		if s.scanned[i].Load() == 0 {
   101  			panic(fmt.Sprintf("missed %d", i))
   102  		}
   103  	}
   104  }