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  }