github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/common.go (about)

     1  package module
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  
     7  	"github.com/onflow/flow-go/module/irrecoverable"
     8  )
     9  
    10  // WARNING: The semantics of this interface will be changing in the near future, with
    11  // startup / shutdown capabilities being delegated to the Startable interface instead.
    12  // For more details, see https://github.com/onflow/flow-go/pull/1167
    13  //
    14  // ReadyDoneAware provides an easy interface to wait for module startup and shutdown.
    15  // Modules that implement this interface only support a single start-stop cycle, and
    16  // will not restart if Ready() is called again after shutdown has already commenced.
    17  type ReadyDoneAware interface {
    18  	// Ready commences startup of the module, and returns a ready channel that is closed once
    19  	// startup has completed. Note that the ready channel may never close if errors are
    20  	// encountered during startup.
    21  	// If shutdown has already commenced before this method is called for the first time,
    22  	// startup will not be performed and the returned channel will also never close.
    23  	// This should be an idempotent method.
    24  	Ready() <-chan struct{}
    25  
    26  	// Done commences shutdown of the module, and returns a done channel that is closed once
    27  	// shutdown has completed. Note that the done channel should be closed even if errors are
    28  	// encountered during shutdown.
    29  	// This should be an idempotent method.
    30  	Done() <-chan struct{}
    31  }
    32  
    33  // NoopReadyDoneAware is a ReadyDoneAware implementation whose ready/done channels close
    34  // immediately
    35  type NoopReadyDoneAware struct{}
    36  
    37  func (n *NoopReadyDoneAware) Ready() <-chan struct{} {
    38  	ready := make(chan struct{})
    39  	defer close(ready)
    40  	return ready
    41  }
    42  
    43  func (n *NoopReadyDoneAware) Done() <-chan struct{} {
    44  	done := make(chan struct{})
    45  	defer close(done)
    46  	return done
    47  }
    48  
    49  // NoopComponent noop struct that implements the component.Component interface.
    50  type NoopComponent struct {
    51  	*NoopReadyDoneAware
    52  }
    53  
    54  func (n *NoopComponent) Start(_ irrecoverable.SignalerContext) {}
    55  
    56  // ProxiedReadyDoneAware is a ReadyDoneAware implementation that proxies the ReadyDoneAware interface
    57  // from another implementation. This allows for usecases where the Ready/Done methods are needed before
    58  // the proxied object is initialized.
    59  type ProxiedReadyDoneAware struct {
    60  	ready chan struct{}
    61  	done  chan struct{}
    62  
    63  	initOnce sync.Once
    64  }
    65  
    66  // NewProxiedReadyDoneAware returns a new ProxiedReadyDoneAware instance
    67  func NewProxiedReadyDoneAware() *ProxiedReadyDoneAware {
    68  	return &ProxiedReadyDoneAware{
    69  		ready: make(chan struct{}),
    70  		done:  make(chan struct{}),
    71  	}
    72  }
    73  
    74  // Init adds the proxied ReadyDoneAware implementation and sets up the ready/done channels
    75  // to close when the respective channel on the proxied object closes.
    76  // Init can only be called once.
    77  //
    78  // IMPORTANT: the proxied ReadyDoneAware implementation must be idempotent since the Ready and Done
    79  // methods will be called immediately when calling Init.
    80  func (n *ProxiedReadyDoneAware) Init(rda ReadyDoneAware) {
    81  	n.initOnce.Do(func() {
    82  		go func() {
    83  			<-rda.Ready()
    84  			close(n.ready)
    85  		}()
    86  		go func() {
    87  			<-rda.Done()
    88  			close(n.done)
    89  		}()
    90  	})
    91  }
    92  
    93  func (n *ProxiedReadyDoneAware) Ready() <-chan struct{} {
    94  	return n.ready
    95  }
    96  
    97  func (n *ProxiedReadyDoneAware) Done() <-chan struct{} {
    98  	return n.done
    99  }
   100  
   101  var ErrMultipleStartup = errors.New("component may only be started once")
   102  
   103  // Startable provides an interface to start a component. Once started, the component
   104  // can be stopped by cancelling the given context.
   105  type Startable interface {
   106  	// Start starts the component. Any irrecoverable errors encountered while the component is running
   107  	// should be thrown with the given SignalerContext.
   108  	// This method should only be called once, and subsequent calls should panic with ErrMultipleStartup.
   109  	Start(irrecoverable.SignalerContext)
   110  }