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 }