github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/runner/single_runner.go (about) 1 package runner 2 3 import ( 4 "sync" 5 ) 6 7 // SingleRunner is a support struct for implementing module.ReadyDoneAware 8 type SingleRunner struct { 9 stateTransition sync.Mutex // lock for preventing concurrent state transitions 10 11 startupCommenced bool // indicates whether Start(...) invoked 12 startupCompleted chan struct{} // channel is closed when startup was completed and the component is properly running 13 14 shutdownCommenced bool // indicates whether Stop() invoked 15 shutdownSignal chan struct{} // used to signal that shutdown has commenced 16 17 shutdownCompleted chan struct{} // used to signal that shutdown was completed and the component is done 18 } 19 20 func NewSingleRunner() SingleRunner { 21 return SingleRunner{ 22 stateTransition: sync.Mutex{}, 23 startupCommenced: false, 24 startupCompleted: make(chan struct{}), 25 shutdownCommenced: false, 26 shutdownSignal: make(chan struct{}), 27 shutdownCompleted: make(chan struct{}), 28 } 29 } 30 31 // ShutdownSignal returns a channel that is closed when the shutdown signal has been given 32 func (u *SingleRunner) ShutdownSignal() <-chan struct{} { 33 return u.shutdownSignal 34 } 35 36 func (s *SingleRunner) Start(f func()) <-chan struct{} { 37 s.stateTransition.Lock() 38 if s.startupCommenced || s.shutdownCommenced { 39 s.stateTransition.Unlock() 40 return s.startupCompleted 41 } 42 43 s.startupCommenced = true 44 go func() { 45 close(s.startupCompleted) 46 s.stateTransition.Unlock() 47 f() 48 // there are two cases f() would exit: 49 // (a) f exited on its own without Abort() being called (this is generally an internal error) 50 // (b) f exited as a reaction to Abort() being called 51 // In either case, we want to abort and close shutdownCompleted 52 s.stateTransition.Lock() 53 s.unsafeCommenceShutdown() 54 close(s.shutdownCompleted) 55 s.stateTransition.Unlock() 56 }() 57 58 return s.startupCompleted 59 } 60 61 // Abort() will abort the SingleRunner. Note that the channel returned from Start() will never be 62 // closed if the SingleRunner is aborted before Start() is called. This mimics the real world case: 63 // - consider a runner at a starting position of a race waiting for the start signal 64 // - if the runner to told to abort the race _before_ the start signal occurred, the runner will never start 65 func (s *SingleRunner) Abort() <-chan struct{} { 66 s.stateTransition.Lock() 67 s.unsafeCommenceShutdown() 68 s.stateTransition.Unlock() 69 return s.shutdownCompleted 70 } 71 72 // unsafeCommenceShutdown executes the shutdown logic once but is not concurrency safe 73 func (s *SingleRunner) unsafeCommenceShutdown() { 74 if !s.shutdownCommenced { 75 s.shutdownCommenced = true 76 close(s.shutdownSignal) 77 } 78 }