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.

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