bitbucket.org/number571/tendermint@v0.8.14/libs/service/service.go (about)

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