6 "github.com/tendermint/tmlibs/log"
9 type Service interface {
27 Classical-inheritance-style service declarations. Services can be started, then
28 stopped, then optionally restarted.
30 Users can override the OnStart/OnStop methods. In the absence of errors, these
31 methods are guaranteed to be called at most once. If OnStart returns an error,
32 service won't be marked as started, so the user can call Start again.
34 A call to Reset will panic, unless OnReset is overwritten, allowing
35 OnStart/OnStop to be called again.
37 The caller must ensure that Start and Stop are not called concurrently.
39 It is ok to call Stop without calling Start first.
43 type FooService struct {
48 func NewFooService() *FooService {
52 fs.BaseService = *NewBaseService(log, "FooService", fs)
56 func (fs *FooService) OnStart() error {
57 fs.BaseService.OnStart() // Always call the overridden method.
58 // initialize private fields
59 // start subroutines, etc.
62 func (fs *FooService) OnStop() error {
63 fs.BaseService.OnStop() // Always call the overridden method.
64 // close/destroy private fields
65 // stop subroutines, etc.
68 type BaseService struct {
71 started uint32 // atomic
72 stopped uint32 // atomic
75 // The "subclass" of BaseService
79 func NewBaseService(logger log.Logger, name string, impl Service) *BaseService {
81 logger = log.NewNopLogger()
87 Quit: make(chan struct{}),
92 func (bs *BaseService) SetLogger(l log.Logger) {
97 func (bs *BaseService) Start() (bool, error) {
98 if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
99 if atomic.LoadUint32(&bs.stopped) == 1 {
100 bs.Logger.Error(Fmt("Not starting %v -- already stopped", bs.name), "impl", bs.impl)
103 bs.Logger.Info(Fmt("Starting %v", bs.name), "impl", bs.impl)
105 err := bs.impl.OnStart()
108 atomic.StoreUint32(&bs.started, 0)
113 bs.Logger.Debug(Fmt("Not starting %v -- already started", bs.name), "impl", bs.impl)
118 // Implements Service
119 // NOTE: Do not put anything in here,
120 // that way users don't need to call BaseService.OnStart()
121 func (bs *BaseService) OnStart() error { return nil }
123 // Implements Service
124 func (bs *BaseService) Stop() bool {
125 if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
126 bs.Logger.Info(Fmt("Stopping %v", bs.name), "impl", bs.impl)
131 bs.Logger.Debug(Fmt("Stopping %v (ignoring: already stopped)", bs.name), "impl", bs.impl)
136 // Implements Service
137 // NOTE: Do not put anything in here,
138 // that way users don't need to call BaseService.OnStop()
139 func (bs *BaseService) OnStop() {}
141 // Implements Service
142 func (bs *BaseService) Reset() (bool, error) {
143 if !atomic.CompareAndSwapUint32(&bs.stopped, 1, 0) {
144 bs.Logger.Debug(Fmt("Can't reset %v. Not stopped", bs.name), "impl", bs.impl)
148 // whether or not we've started, we can reset
149 atomic.CompareAndSwapUint32(&bs.started, 1, 0)
151 bs.Quit = make(chan struct{})
152 return true, bs.impl.OnReset()
155 // Implements Service
156 func (bs *BaseService) OnReset() error {
157 PanicSanity("The service cannot be reset")
161 // Implements Service
162 func (bs *BaseService) IsRunning() bool {
163 return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
166 func (bs *BaseService) Wait() {
171 func (bs *BaseService) String() string {
175 //----------------------------------------
177 type QuitService struct {
181 func NewQuitService(logger log.Logger, name string, impl Service) *QuitService {
183 logger.Info("QuitService is deprecated, use BaseService instead")
186 BaseService: *NewBaseService(logger, name, impl),