service: change stop interface (#7816)

This commit is contained in:
Sam Kleinman
2022-02-17 11:23:32 -05:00
committed by GitHub
parent 38e29590ff
commit 28d34d635c
30 changed files with 129 additions and 226 deletions

View File

@@ -47,7 +47,7 @@ type Fireable interface {
type EventSwitch interface {
service.Service
Fireable
Stop() error
Stop()
AddListenerForEvent(listenerID, eventValue string, cb EventCallback) error
RemoveListenerForEvent(event string, listenerID string)

View File

@@ -9,15 +9,9 @@ import (
)
var (
// ErrAlreadyStarted is returned when somebody tries to start an already
// running service.
ErrAlreadyStarted = errors.New("already started")
// ErrAlreadyStopped is returned when somebody tries to stop an already
// errAlreadyStopped is returned when somebody tries to stop an already
// stopped service (without resetting it).
ErrAlreadyStopped = errors.New("already stopped")
// ErrNotStarted is returned when somebody tries to stop a not running
// service.
ErrNotStarted = errors.New("not started")
errAlreadyStopped = errors.New("already stopped")
)
// Service defines a service that can be started, stopped, and reset.
@@ -46,13 +40,14 @@ type Implementation interface {
/*
Classical-inheritance-style service declarations. Services can be started, then
stopped, then optionally restarted.
stopped, but cannot be restarted.
Users can override the OnStart/OnStop methods. In the absence of errors, these
Users must implement OnStart/OnStop methods. In the absence of errors, these
methods are guaranteed to be called at most once. If OnStart returns an error,
service won't be marked as started, so the user can call Start again.
It is safe, but an error, to call Stop without calling Start first.
The BaseService implementation ensures that the OnStop method is
called after the context passed to Start is canceled.
Typical usage:
@@ -74,7 +69,7 @@ Typical usage:
// start subroutines, etc.
}
func (fs *FooService) OnStop() error {
func (fs *FooService) OnStop() {
// close/destroy private fields
// stop subroutines, etc.
}
@@ -99,20 +94,20 @@ func NewBaseService(logger log.Logger, name string, impl Implementation) *BaseSe
}
}
// Start starts the Service and calls its OnStart method. An error will be
// returned if the service is already running or stopped. To restart a
// stopped service, call Reset.
// Start starts the Service and calls its OnStart method. An error
// will be returned if the service is stopped, but not if it is
// already running.
func (bs *BaseService) Start(ctx context.Context) error {
bs.mtx.Lock()
defer bs.mtx.Unlock()
if bs.quit != nil {
return ErrAlreadyStarted
return nil
}
select {
case <-bs.quit:
return ErrAlreadyStopped
return errAlreadyStopped
default:
bs.logger.Info("starting service", "service", bs.name, "impl", bs.name)
if err := bs.impl.OnStart(ctx); err != nil {
@@ -132,7 +127,7 @@ func (bs *BaseService) Start(ctx context.Context) error {
// this means stop was called manually
return
case <-ctx.Done():
_ = bs.Stop()
bs.Stop()
}
bs.logger.Info("stopped service",
@@ -143,25 +138,26 @@ func (bs *BaseService) Start(ctx context.Context) error {
}
}
// Stop implements Service by calling OnStop (if defined) and closing quit
// channel. An error will be returned if the service is already stopped.
func (bs *BaseService) Stop() error {
// Stop manually terminates the service by calling OnStop method from
// the implementation and releases all resources related to the
// service.
func (bs *BaseService) Stop() {
bs.mtx.Lock()
defer bs.mtx.Unlock()
if bs.quit == nil {
return ErrNotStarted
return
}
select {
case <-bs.quit:
return ErrAlreadyStopped
return
default:
bs.logger.Info("stopping service", "service", bs.name)
bs.impl.OnStop()
bs.cancel()
return nil
return
}
}

View File

@@ -96,7 +96,7 @@ func TestBaseService(t *testing.T) {
require.True(t, ts.isStarted())
require.NoError(t, ts.Stop())
ts.Stop()
require.True(t, ts.isStopped())
require.False(t, ts.IsRunning())
})
@@ -107,10 +107,10 @@ func TestBaseService(t *testing.T) {
require.NoError(t, ts.Start(ctx))
require.True(t, ts.isStarted())
require.NoError(t, ts.Stop())
ts.Stop()
require.True(t, ts.isStopped())
require.False(t, ts.isMultiStopped())
require.Error(t, ts.Stop())
ts.Stop()
require.False(t, ts.isMultiStopped())
})
t.Run("MultiThreaded", func(t *testing.T) {
@@ -123,7 +123,7 @@ func TestBaseService(t *testing.T) {
require.NoError(t, ts.Start(ctx))
require.True(t, ts.isStarted())
go func() { _ = ts.Stop() }()
go ts.Stop()
go cancel()
ts.Wait()