You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
4.6 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "sync"
  6. "github.com/tendermint/tendermint/libs/log"
  7. )
  8. var (
  9. // ErrAlreadyStarted is returned when somebody tries to start an already
  10. // running service.
  11. ErrAlreadyStarted = errors.New("already started")
  12. // ErrAlreadyStopped is returned when somebody tries to stop an already
  13. // stopped service (without resetting it).
  14. ErrAlreadyStopped = errors.New("already stopped")
  15. // ErrNotStarted is returned when somebody tries to stop a not running
  16. // service.
  17. ErrNotStarted = errors.New("not started")
  18. )
  19. // Service defines a service that can be started, stopped, and reset.
  20. type Service interface {
  21. // Start is called to start the service, which should run until
  22. // the context terminates. If the service is already running, Start
  23. // must report an error.
  24. Start(context.Context) error
  25. // Return true if the service is running
  26. IsRunning() bool
  27. // Wait blocks until the service is stopped.
  28. Wait()
  29. }
  30. // Implementation describes the implementation that the
  31. // BaseService implementation wraps.
  32. type Implementation interface {
  33. // Called by the Services Start Method
  34. OnStart(context.Context) error
  35. // Called when the service's context is canceled.
  36. OnStop()
  37. }
  38. /*
  39. Classical-inheritance-style service declarations. Services can be started, then
  40. stopped, then optionally restarted.
  41. Users can override the OnStart/OnStop methods. In the absence of errors, these
  42. methods are guaranteed to be called at most once. If OnStart returns an error,
  43. service won't be marked as started, so the user can call Start again.
  44. It is safe, but an error, to call Stop without calling Start first.
  45. Typical usage:
  46. type FooService struct {
  47. BaseService
  48. // private fields
  49. }
  50. func NewFooService() *FooService {
  51. fs := &FooService{
  52. // init
  53. }
  54. fs.BaseService = *NewBaseService(log, "FooService", fs)
  55. return fs
  56. }
  57. func (fs *FooService) OnStart(ctx context.Context) error {
  58. // initialize private fields
  59. // start subroutines, etc.
  60. }
  61. func (fs *FooService) OnStop() error {
  62. // close/destroy private fields
  63. // stop subroutines, etc.
  64. }
  65. */
  66. type BaseService struct {
  67. logger log.Logger
  68. name string
  69. mtx sync.Mutex
  70. quit <-chan (struct{})
  71. cancel context.CancelFunc
  72. // The "subclass" of BaseService
  73. impl Implementation
  74. }
  75. // NewBaseService creates a new BaseService.
  76. func NewBaseService(logger log.Logger, name string, impl Implementation) *BaseService {
  77. return &BaseService{
  78. logger: logger,
  79. name: name,
  80. impl: impl,
  81. }
  82. }
  83. // Start starts the Service and calls its OnStart method. An error will be
  84. // returned if the service is already running or stopped. To restart a
  85. // stopped service, call Reset.
  86. func (bs *BaseService) Start(ctx context.Context) error {
  87. bs.mtx.Lock()
  88. defer bs.mtx.Unlock()
  89. if bs.quit != nil {
  90. return ErrAlreadyStarted
  91. }
  92. select {
  93. case <-bs.quit:
  94. return ErrAlreadyStopped
  95. default:
  96. bs.logger.Info("starting service", "service", bs.name, "impl", bs.name)
  97. if err := bs.impl.OnStart(ctx); err != nil {
  98. return err
  99. }
  100. // we need a separate context to ensure that we start
  101. // a thread that will get cleaned up and that the
  102. // Stop/Wait functions work as expected.
  103. srvCtx, cancel := context.WithCancel(context.Background())
  104. bs.cancel = cancel
  105. bs.quit = srvCtx.Done()
  106. go func(ctx context.Context) {
  107. select {
  108. case <-srvCtx.Done():
  109. // this means stop was called manually
  110. return
  111. case <-ctx.Done():
  112. _ = bs.Stop()
  113. }
  114. bs.logger.Info("stopped service",
  115. "service", bs.name)
  116. }(ctx)
  117. return nil
  118. }
  119. }
  120. // Stop implements Service by calling OnStop (if defined) and closing quit
  121. // channel. An error will be returned if the service is already stopped.
  122. func (bs *BaseService) Stop() error {
  123. bs.mtx.Lock()
  124. defer bs.mtx.Unlock()
  125. if bs.quit == nil {
  126. return ErrNotStarted
  127. }
  128. select {
  129. case <-bs.quit:
  130. return ErrAlreadyStopped
  131. default:
  132. bs.logger.Info("stopping service", "service", bs.name)
  133. bs.impl.OnStop()
  134. bs.cancel()
  135. return nil
  136. }
  137. }
  138. // IsRunning implements Service by returning true or false depending on the
  139. // service's state.
  140. func (bs *BaseService) IsRunning() bool {
  141. bs.mtx.Lock()
  142. defer bs.mtx.Unlock()
  143. if bs.quit == nil {
  144. return false
  145. }
  146. select {
  147. case <-bs.quit:
  148. return false
  149. default:
  150. return true
  151. }
  152. }
  153. func (bs *BaseService) getWait() <-chan struct{} {
  154. bs.mtx.Lock()
  155. defer bs.mtx.Unlock()
  156. if bs.quit == nil {
  157. out := make(chan struct{})
  158. close(out)
  159. return out
  160. }
  161. return bs.quit
  162. }
  163. // Wait blocks until the service is stopped.
  164. func (bs *BaseService) Wait() { <-bs.getWait() }
  165. // String implements Service by returning a string representation of the service.
  166. func (bs *BaseService) String() string { return bs.name }