OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / tmlibs / common / service.go
1 package common
2
3 import (
4         "sync/atomic"
5
6         "github.com/tendermint/tmlibs/log"
7 )
8
9 type Service interface {
10         Start() (bool, error)
11         OnStart() error
12
13         Stop() bool
14         OnStop()
15
16         Reset() (bool, error)
17         OnReset() error
18
19         IsRunning() bool
20
21         String() string
22
23         SetLogger(log.Logger)
24 }
25
26 /*
27 Classical-inheritance-style service declarations. Services can be started, then
28 stopped, then optionally restarted.
29
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.
33
34 A call to Reset will panic, unless OnReset is overwritten, allowing
35 OnStart/OnStop to be called again.
36
37 The caller must ensure that Start and Stop are not called concurrently.
38
39 It is ok to call Stop without calling Start first.
40
41 Typical usage:
42
43         type FooService struct {
44                 BaseService
45                 // private fields
46         }
47
48         func NewFooService() *FooService {
49                 fs := &FooService{
50                         // init
51                 }
52                 fs.BaseService = *NewBaseService(log, "FooService", fs)
53                 return fs
54         }
55
56         func (fs *FooService) OnStart() error {
57                 fs.BaseService.OnStart() // Always call the overridden method.
58                 // initialize private fields
59                 // start subroutines, etc.
60         }
61
62         func (fs *FooService) OnStop() error {
63                 fs.BaseService.OnStop() // Always call the overridden method.
64                 // close/destroy private fields
65                 // stop subroutines, etc.
66         }
67 */
68 type BaseService struct {
69         Logger  log.Logger
70         name    string
71         started uint32 // atomic
72         stopped uint32 // atomic
73         Quit    chan struct{}
74
75         // The "subclass" of BaseService
76         impl Service
77 }
78
79 func NewBaseService(logger log.Logger, name string, impl Service) *BaseService {
80         if logger == nil {
81                 logger = log.NewNopLogger()
82         }
83
84         return &BaseService{
85                 Logger: logger,
86                 name:   name,
87                 Quit:   make(chan struct{}),
88                 impl:   impl,
89         }
90 }
91
92 func (bs *BaseService) SetLogger(l log.Logger) {
93         bs.Logger = l
94 }
95
96 // Implements Servce
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)
101                         return false, nil
102                 } else {
103                         bs.Logger.Info(Fmt("Starting %v", bs.name), "impl", bs.impl)
104                 }
105                 err := bs.impl.OnStart()
106                 if err != nil {
107                         // revert flag
108                         atomic.StoreUint32(&bs.started, 0)
109                         return false, err
110                 }
111                 return true, err
112         } else {
113                 bs.Logger.Debug(Fmt("Not starting %v -- already started", bs.name), "impl", bs.impl)
114                 return false, nil
115         }
116 }
117
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 }
122
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)
127                 bs.impl.OnStop()
128                 close(bs.Quit)
129                 return true
130         } else {
131                 bs.Logger.Debug(Fmt("Stopping %v (ignoring: already stopped)", bs.name), "impl", bs.impl)
132                 return false
133         }
134 }
135
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() {}
140
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)
145                 return false, nil
146         }
147
148         // whether or not we've started, we can reset
149         atomic.CompareAndSwapUint32(&bs.started, 1, 0)
150
151         bs.Quit = make(chan struct{})
152         return true, bs.impl.OnReset()
153 }
154
155 // Implements Service
156 func (bs *BaseService) OnReset() error {
157         PanicSanity("The service cannot be reset")
158         return nil
159 }
160
161 // Implements Service
162 func (bs *BaseService) IsRunning() bool {
163         return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
164 }
165
166 func (bs *BaseService) Wait() {
167         <-bs.Quit
168 }
169
170 // Implements Servce
171 func (bs *BaseService) String() string {
172         return bs.name
173 }
174
175 //----------------------------------------
176
177 type QuitService struct {
178         BaseService
179 }
180
181 func NewQuitService(logger log.Logger, name string, impl Service) *QuitService {
182         if logger != nil {
183                 logger.Info("QuitService is deprecated, use BaseService instead")
184         }
185         return &QuitService{
186                 BaseService: *NewBaseService(logger, name, impl),
187         }
188 }