github.com/vipernet-xyz/tm@v0.34.24/libs/service/service.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync/atomic"
     7  
     8  	"github.com/vipernet-xyz/tm/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("service start",
   140  			"msg",
   141  			log.NewLazySprintf("Starting %v service", bs.name),
   142  			"impl",
   143  			bs.impl.String())
   144  		err := bs.impl.OnStart()
   145  		if err != nil {
   146  			// revert flag
   147  			atomic.StoreUint32(&bs.started, 0)
   148  			return err
   149  		}
   150  		return nil
   151  	}
   152  	bs.Logger.Debug("service start",
   153  		"msg",
   154  		log.NewLazySprintf("Not starting %v service -- already started", bs.name),
   155  		"impl",
   156  		bs.impl)
   157  	return ErrAlreadyStarted
   158  }
   159  
   160  // OnStart implements Service by doing nothing.
   161  // NOTE: Do not put anything in here,
   162  // that way users don't need to call BaseService.OnStart()
   163  func (bs *BaseService) OnStart() error { return nil }
   164  
   165  // Stop implements Service by calling OnStop (if defined) and closing quit
   166  // channel. An error will be returned if the service is already stopped.
   167  func (bs *BaseService) Stop() error {
   168  	if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
   169  		if atomic.LoadUint32(&bs.started) == 0 {
   170  			bs.Logger.Error(fmt.Sprintf("Not stopping %v service -- has not been started yet", bs.name),
   171  				"impl", bs.impl)
   172  			// revert flag
   173  			atomic.StoreUint32(&bs.stopped, 0)
   174  			return ErrNotStarted
   175  		}
   176  		bs.Logger.Info("service stop",
   177  			"msg",
   178  			log.NewLazySprintf("Stopping %v service", bs.name),
   179  			"impl",
   180  			bs.impl)
   181  		bs.impl.OnStop()
   182  		close(bs.quit)
   183  		return nil
   184  	}
   185  	bs.Logger.Debug("service stop",
   186  		"msg",
   187  		log.NewLazySprintf("Stopping %v service (already stopped)", bs.name),
   188  		"impl",
   189  		bs.impl)
   190  	return ErrAlreadyStopped
   191  }
   192  
   193  // OnStop implements Service by doing nothing.
   194  // NOTE: Do not put anything in here,
   195  // that way users don't need to call BaseService.OnStop()
   196  func (bs *BaseService) OnStop() {}
   197  
   198  // Reset implements Service by calling OnReset callback (if defined). An error
   199  // will be returned if the service is running.
   200  func (bs *BaseService) Reset() error {
   201  	if !atomic.CompareAndSwapUint32(&bs.stopped, 1, 0) {
   202  		bs.Logger.Debug("service reset",
   203  			"msg",
   204  			log.NewLazySprintf("Can't reset %v service. Not stopped", bs.name),
   205  			"impl",
   206  			bs.impl)
   207  		return fmt.Errorf("can't reset running %s", bs.name)
   208  	}
   209  
   210  	// whether or not we've started, we can reset
   211  	atomic.CompareAndSwapUint32(&bs.started, 1, 0)
   212  
   213  	bs.quit = make(chan struct{})
   214  	return bs.impl.OnReset()
   215  }
   216  
   217  // OnReset implements Service by panicking.
   218  func (bs *BaseService) OnReset() error {
   219  	panic("The service cannot be reset")
   220  }
   221  
   222  // IsRunning implements Service by returning true or false depending on the
   223  // service's state.
   224  func (bs *BaseService) IsRunning() bool {
   225  	return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
   226  }
   227  
   228  // Wait blocks until the service is stopped.
   229  func (bs *BaseService) Wait() {
   230  	<-bs.quit
   231  }
   232  
   233  // String implements Service by returning a string representation of the service.
   234  func (bs *BaseService) String() string {
   235  	return bs.name
   236  }
   237  
   238  // Quit Implements Service by returning a quit channel.
   239  func (bs *BaseService) Quit() <-chan struct{} {
   240  	return bs.quit
   241  }