diff --git a/internal/httpproxy/loop.go b/internal/httpproxy/loop.go index 3e9870d6..01fb0ff3 100644 --- a/internal/httpproxy/loop.go +++ b/internal/httpproxy/loop.go @@ -3,11 +3,12 @@ package httpproxy import ( "context" - "sync" "time" "github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/httpproxy/state" + "github.com/qdm12/gluetun/internal/loopstate" "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/golibs/logging" ) @@ -23,11 +24,11 @@ type Looper interface { } type looper struct { - state state + statusManager loopstate.Manager + state state.Manager // Other objects logger logging.Logger // Internal channels and locks - loopLock sync.Mutex running chan models.LoopStatus stop, stopped chan struct{} start chan struct{} @@ -37,17 +38,24 @@ type looper struct { const defaultBackoffTime = 10 * time.Second func NewLooper(logger logging.Logger, settings configuration.HTTPProxy) Looper { + start := make(chan struct{}) + running := make(chan models.LoopStatus) + stop := make(chan struct{}) + stopped := make(chan struct{}) + + statusManager := loopstate.New(constants.Stopped, + start, running, stop, stopped) + state := state.New(statusManager, settings) + return &looper{ - state: state{ - status: constants.Stopped, - settings: settings, - }, - logger: logger, - start: make(chan struct{}), - running: make(chan models.LoopStatus), - stop: make(chan struct{}), - stopped: make(chan struct{}), - backoffTime: defaultBackoffTime, + statusManager: statusManager, + state: state, + logger: logger, + start: start, + running: running, + stop: stop, + stopped: stopped, + backoffTime: defaultBackoffTime, } } diff --git a/internal/httpproxy/run.go b/internal/httpproxy/run.go index e2ddc758..10ac9bbd 100644 --- a/internal/httpproxy/run.go +++ b/internal/httpproxy/run.go @@ -14,7 +14,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { if l.GetSettings().Enabled { go func() { - _, _ = l.SetStatus(ctx, constants.Running) + _, _ = l.statusManager.ApplyStatus(ctx, constants.Running) }() } @@ -40,7 +40,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { crashed = false } else { l.backoffTime = defaultBackoffTime - l.state.setStatusWithLock(constants.Running) + l.statusManager.SetStatus(constants.Running) } stayHere := true @@ -61,7 +61,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { <-errorCh l.stopped <- struct{}{} case err := <-errorCh: - l.state.setStatusWithLock(constants.Crashed) + l.statusManager.SetStatus(constants.Crashed) l.logAndWait(ctx, err) crashed = true stayHere = false diff --git a/internal/httpproxy/settings.go b/internal/httpproxy/settings.go index 02763b59..4d75b504 100644 --- a/internal/httpproxy/settings.go +++ b/internal/httpproxy/settings.go @@ -2,40 +2,15 @@ package httpproxy import ( "context" - "reflect" "github.com/qdm12/gluetun/internal/configuration" - "github.com/qdm12/gluetun/internal/constants" ) func (l *looper) GetSettings() (settings configuration.HTTPProxy) { - l.state.settingsMu.RLock() - defer l.state.settingsMu.RUnlock() - return l.state.settings + return l.state.GetSettings() } func (l *looper) SetSettings(ctx context.Context, settings configuration.HTTPProxy) ( outcome string) { - l.state.settingsMu.Lock() - settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) - if settingsUnchanged { - l.state.settingsMu.Unlock() - return "settings left unchanged" - } - newEnabled := settings.Enabled - previousEnabled := l.state.settings.Enabled - l.state.settings = settings - l.state.settingsMu.Unlock() - // Either restart or set changed status - switch { - case !newEnabled && !previousEnabled: - case newEnabled && previousEnabled: - _, _ = l.SetStatus(ctx, constants.Stopped) - _, _ = l.SetStatus(ctx, constants.Running) - case newEnabled && !previousEnabled: - _, _ = l.SetStatus(ctx, constants.Running) - case !newEnabled && previousEnabled: - _, _ = l.SetStatus(ctx, constants.Stopped) - } - return "settings updated" + return l.state.SetSettings(ctx, settings) } diff --git a/internal/httpproxy/state.go b/internal/httpproxy/state.go deleted file mode 100644 index 4d0cafee..00000000 --- a/internal/httpproxy/state.go +++ /dev/null @@ -1,15 +0,0 @@ -package httpproxy - -import ( - "sync" - - "github.com/qdm12/gluetun/internal/configuration" - "github.com/qdm12/gluetun/internal/models" -) - -type state struct { - status models.LoopStatus - settings configuration.HTTPProxy - statusMu sync.RWMutex - settingsMu sync.RWMutex -} diff --git a/internal/httpproxy/state/settings.go b/internal/httpproxy/state/settings.go new file mode 100644 index 00000000..8ac83c86 --- /dev/null +++ b/internal/httpproxy/state/settings.go @@ -0,0 +1,55 @@ +package state + +import ( + "context" + "reflect" + + "github.com/qdm12/gluetun/internal/configuration" + "github.com/qdm12/gluetun/internal/constants" +) + +type SettingsGetterSetter interface { + SettingsGetter + SettingsSetter +} + +type SettingsGetter interface { + GetSettings() (settings configuration.HTTPProxy) +} + +func (s *State) GetSettings() (settings configuration.HTTPProxy) { + s.settingsMu.RLock() + defer s.settingsMu.RUnlock() + return s.settings +} + +type SettingsSetter interface { + SetSettings(ctx context.Context, + settings configuration.HTTPProxy) (outcome string) +} + +func (s *State) SetSettings(ctx context.Context, + settings configuration.HTTPProxy) (outcome string) { + s.settingsMu.Lock() + settingsUnchanged := reflect.DeepEqual(settings, s.settings) + if settingsUnchanged { + s.settingsMu.Unlock() + return "settings left unchanged" + } + newEnabled := settings.Enabled + previousEnabled := s.settings.Enabled + s.settings = settings + s.settingsMu.Unlock() + // Either restart or set changed status + switch { + case !newEnabled && !previousEnabled: + case newEnabled && previousEnabled: + _, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped) + _, _ = s.statusApplier.ApplyStatus(ctx, constants.Running) + case newEnabled && !previousEnabled: + _, _ = s.statusApplier.ApplyStatus(ctx, constants.Running) + case !newEnabled && previousEnabled: + _, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped) + } + return "settings updated" +} diff --git a/internal/httpproxy/status.go b/internal/httpproxy/status.go index 0d44ec42..1d40920b 100644 --- a/internal/httpproxy/status.go +++ b/internal/httpproxy/status.go @@ -2,75 +2,15 @@ package httpproxy import ( "context" - "errors" - "fmt" - "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" ) -func (s *state) setStatusWithLock(status models.LoopStatus) { - s.statusMu.Lock() - defer s.statusMu.Unlock() - s.status = status -} - func (l *looper) GetStatus() (status models.LoopStatus) { - l.state.statusMu.RLock() - defer l.state.statusMu.RUnlock() - return l.state.status + return l.statusManager.GetStatus() } -var ErrInvalidStatus = errors.New("invalid status") - func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( outcome string, err error) { - l.state.statusMu.Lock() - defer l.state.statusMu.Unlock() - existingStatus := l.state.status - - switch status { - case constants.Running: - switch existingStatus { - case constants.Starting, constants.Running, constants.Stopping, constants.Crashed: - return fmt.Sprintf("already %s", existingStatus), nil - } - l.loopLock.Lock() - defer l.loopLock.Unlock() - l.state.status = constants.Starting - l.state.statusMu.Unlock() - l.start <- struct{}{} - - newStatus := constants.Starting // for canceled context - select { - case <-ctx.Done(): - case newStatus = <-l.running: - } - l.state.statusMu.Lock() - l.state.status = newStatus - return newStatus.String(), nil - case constants.Stopped: - switch existingStatus { - case constants.Stopped, constants.Stopping, constants.Starting, constants.Crashed: - return fmt.Sprintf("already %s", existingStatus), nil - } - l.loopLock.Lock() - defer l.loopLock.Unlock() - l.state.status = constants.Stopping - l.state.statusMu.Unlock() - l.stop <- struct{}{} - - newStatus := constants.Stopping // for canceled context - select { - case <-ctx.Done(): - case <-l.stopped: - newStatus = constants.Stopped - } - l.state.statusMu.Lock() - l.state.status = newStatus - return status.String(), nil - default: - return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", - ErrInvalidStatus, status, constants.Running, constants.Stopped) - } + return l.statusManager.ApplyStatus(ctx, status) }