| 34 | var ErrInvalidStatus = errors.New("invalid status") |
| 35 | |
| 36 | func (l *Loop) SetStatus(ctx context.Context, status models.LoopStatus) ( |
| 37 | outcome string, err error, |
| 38 | ) { |
| 39 | l.state.statusMu.Lock() |
| 40 | defer l.state.statusMu.Unlock() |
| 41 | existingStatus := l.state.status |
| 42 | |
| 43 | switch status { |
| 44 | case constants.Running: |
| 45 | switch existingStatus { |
| 46 | case constants.Starting, constants.Running, constants.Stopping, constants.Crashed: |
| 47 | return fmt.Sprintf("already %s", existingStatus), nil |
| 48 | } |
| 49 | l.loopLock.Lock() |
| 50 | defer l.loopLock.Unlock() |
| 51 | l.state.status = constants.Starting |
| 52 | l.state.statusMu.Unlock() |
| 53 | l.start <- struct{}{} |
| 54 | |
| 55 | newStatus := constants.Starting // for canceled context |
| 56 | select { |
| 57 | case <-ctx.Done(): |
| 58 | case newStatus = <-l.running: |
| 59 | } |
| 60 | l.state.statusMu.Lock() |
| 61 | l.state.status = newStatus |
| 62 | return newStatus.String(), nil |
| 63 | case constants.Stopped: |
| 64 | switch existingStatus { |
| 65 | case constants.Stopped, constants.Stopping, constants.Starting, constants.Crashed: |
| 66 | return fmt.Sprintf("already %s", existingStatus), nil |
| 67 | } |
| 68 | l.loopLock.Lock() |
| 69 | defer l.loopLock.Unlock() |
| 70 | l.state.status = constants.Stopping |
| 71 | l.state.statusMu.Unlock() |
| 72 | l.stop <- struct{}{} |
| 73 | newStatus := constants.Stopping // for canceled context |
| 74 | select { |
| 75 | case <-ctx.Done(): |
| 76 | case <-l.stopped: |
| 77 | newStatus = constants.Stopped |
| 78 | } |
| 79 | l.state.statusMu.Lock() |
| 80 | l.state.status = newStatus |
| 81 | return status.String(), nil |
| 82 | default: |
| 83 | return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", |
| 84 | ErrInvalidStatus, status, constants.Running, constants.Stopped) |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | func (l *Loop) GetSettings() (settings settings.Shadowsocks) { |
| 89 | l.state.settingsMu.RLock() |