github.com/vipernet-xyz/tendermint-core@v0.32.0/libs/service/service.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync/atomic"
     7  
     8  	"github.com/tendermint/tendermint/libs/log"
     9  )
    10  
    11  var (
    12  	// ErrAlreadyStarted is returned when somebody tries to start an already
    13  	// running service.
    14  	ErrAlreadyStarted = errors.New("already started")
    15  	// ErrAlreadyStopped is returned when somebody tries to stop an already
    16  	// stopped service (without resetting it).
    17  	ErrAlreadyStopped = errors.New("already stopped")
    18  	// ErrNotStarted is returned when somebody tries to stop a not running
    19  	// service.
    20  	ErrNotStarted = errors.New("not started")
    21  )
    22  
    23  // Service defines a service that can be started, stopped, and reset.
    24  type Service interface {
    25  	// Start the service.
    26  	// If it's already started or stopped, will return an error.
    27  	// If OnStart() returns an error, it's returned by Start()
    28  	Start() error
    29  	OnStart() error
    30  
    31  	// Stop the service.
    32  	// If it's already stopped, will return an error.
    33  	// OnStop must never error.
    34  	Stop() error
    35  	OnStop()
    36  
    37  	// Reset the service.
    38  	// Panics by default - must be overwritten to enable reset.
    39  	Reset() error
    40  	OnReset() error
    41  
    42  	// Return true if the service is running
    43  	IsRunning() bool
    44  
    45  	// Quit returns a channel, which is closed once service is stopped.
    46  	Quit() <-chan struct{}
    47  
    48  	// String representation of the service
    49  	String() string
    50  
    51  	// SetLogger sets a logger.
    52  	SetLogger(log.Logger)
    53  }
    54  
    55  /*
    56  Classical-inheritance-style service declarations. Services can be started, then
    57  stopped, then optionally restarted.
    58  
    59  Users can override the OnStart/OnStop methods. In the absence of errors, these
    60  methods are guaranteed to be called at most once. If OnStart returns an error,
    61  service won't be marked as started, so the user can call Start again.
    62  
    63  A call to Reset will panic, unless OnReset is overwritten, allowing
    64  OnStart/OnStop to be called again.
    65  
    66  The caller must ensure that Start and Stop are not called concurrently.
    67  
    68  It is ok to call Stop without calling Start first.
    69  
    70  Typical usage:
    71  
    72  	type FooService struct {
    73  		BaseService
    74  		// private fields
    75  	}
    76  
    77  	func NewFooService() *FooService {
    78  		fs := &FooService{
    79  			// init
    80  		}
    81  		fs.BaseService = *NewBaseService(log, "FooService", fs)
    82  		return fs
    83  	}
    84  
    85  	func (fs *FooService) OnStart() error {
    86  		fs.BaseService.OnStart() // Always call the overridden method.
    87  		// initialize private fields
    88  		// start subroutines, etc.
    89  	}
    90  
    91  	func (fs *FooService) OnStop() error {
    92  		fs.BaseService.OnStop() // Always call the overridden method.
    93  		// close/destroy private fields
    94  		// stop subroutines, etc.
    95  	}
    96  */
    97  type BaseService struct {
    98  	Logger  log.Logger
    99  	name    string
   100  	started uint32 // atomic
   101  	stopped uint32 // atomic
   102  	quit    chan struct{}
   103  
   104  	// The "subclass" of BaseService
   105  	impl Service
   106  }
   107  
   108  // NewBaseService creates a new BaseService.
   109  func NewBaseService(logger log.Logger, name string, impl Service) *BaseService {
   110  	if logger == nil {
   111  		logger = log.NewNopLogger()
   112  	}
   113  
   114  	return &BaseService{
   115  		Logger: logger,
   116  		name:   name,
   117  		quit:   make(chan struct{}),
   118  		impl:   impl,
   119  	}
   120  }
   121  
   122  // SetLogger implements Service by setting a logger.
   123  func (bs *BaseService) SetLogger(l log.Logger) {
   124  	bs.Logger = l
   125  }
   126  
   127  // Start implements Service by calling OnStart (if defined). An error will be
   128  // returned if the service is already running or stopped. Not to start the
   129  // stopped service, you need to call Reset.
   130  func (bs *BaseService) Start() error {
   131  	if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
   132  		if atomic.LoadUint32(&bs.stopped) == 1 {
   133  			bs.Logger.Error(fmt.Sprintf("Not starting %v service -- already stopped", bs.name),
   134  				"impl", bs.impl)
   135  			// revert flag
   136  			atomic.StoreUint32(&bs.started, 0)
   137  			return ErrAlreadyStopped
   138  		}
   139  		bs.Logger.Info(fmt.Sprintf("Starting %v service", bs.name), "impl", bs.impl)
   140  		err := bs.impl.OnStart()
   141  		if err != nil {
   142  			// revert flag
   143  			atomic.StoreUint32(&bs.started, 0)
   144  			return err
   145  		}
   146  		return nil
   147  	}
   148  	bs.Logger.Debug(fmt.Sprintf("Not starting %v service -- already started", bs.name), "impl", bs.impl)
   149  	return ErrAlreadyStarted
   150  }
   151  
   152  // OnStart implements Service by doing nothing.
   153  // NOTE: Do not put anything in here,
   154  // that way users don't need to call BaseService.OnStart()
   155  func (bs *BaseService) OnStart() error { return nil }
   156  
   157  // Stop implements Service by calling OnStop (if defined) and closing quit
   158  // channel. An error will be returned if the service is already stopped.
   159  func (bs *BaseService) Stop() error {
   160  	if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
   161  		if atomic.LoadUint32(&bs.started) == 0 {
   162  			bs.Logger.Error(fmt.Sprintf("Not stopping %v service -- has not been started yet", bs.name),
   163  				"impl", bs.impl)
   164  			// revert flag
   165  			atomic.StoreUint32(&bs.stopped, 0)
   166  			return ErrNotStarted
   167  		}
   168  		bs.Logger.Info(fmt.Sprintf("Stopping %v service", bs.name), "impl", bs.impl)
   169  		bs.impl.OnStop()
   170  		close(bs.quit)
   171  		return nil
   172  	}
   173  	bs.Logger.Debug(fmt.Sprintf("Stopping %v service (already stopped)", bs.name), "impl", bs.impl)
   174  	return ErrAlreadyStopped
   175  }
   176  
   177  // OnStop implements Service by doing nothing.
   178  // NOTE: Do not put anything in here,
   179  // that way users don't need to call BaseService.OnStop()
   180  func (bs *BaseService) OnStop() {}
   181  
   182  // Reset implements Service by calling OnReset callback (if defined). An error
   183  // will be returned if the service is running.
   184  func (bs *BaseService) Reset() error {
   185  	if !atomic.CompareAndSwapUint32(&bs.stopped, 1, 0) {
   186  		bs.Logger.Debug(fmt.Sprintf("Can't reset %v service. Not stopped", bs.name), "impl", bs.impl)
   187  		return fmt.Errorf("can't reset running %s", bs.name)
   188  	}
   189  
   190  	// whether or not we've started, we can reset
   191  	atomic.CompareAndSwapUint32(&bs.started, 1, 0)
   192  
   193  	bs.quit = make(chan struct{})
   194  	return bs.impl.OnReset()
   195  }
   196  
   197  // OnReset implements Service by panicking.
   198  func (bs *BaseService) OnReset() error {
   199  	panic("The service cannot be reset")
   200  }
   201  
   202  // IsRunning implements Service by returning true or false depending on the
   203  // service's state.
   204  func (bs *BaseService) IsRunning() bool {
   205  	return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
   206  }
   207  
   208  // Wait blocks until the service is stopped.
   209  func (bs *BaseService) Wait() {
   210  	<-bs.quit
   211  }
   212  
   213  // String implements Service by returning a string representation of the service.
   214  func (bs *BaseService) String() string {
   215  	return bs.name
   216  }
   217  
   218  // Quit Implements Service by returning a quit channel.
   219  func (bs *BaseService) Quit() <-chan struct{} {
   220  	return bs.quit
   221  }