1// Copyright (C) 2015 The Syncthing Authors. 2// 3// This Source Code Form is subject to the terms of the Mozilla Public 4// License, v. 2.0. If a copy of the MPL was not distributed with this file, 5// You can obtain one at https://mozilla.org/MPL/2.0/. 6 7package model 8 9import ( 10 "time" 11 12 "github.com/syncthing/syncthing/lib/events" 13 "github.com/syncthing/syncthing/lib/sync" 14) 15 16type folderState int 17 18const ( 19 FolderIdle folderState = iota 20 FolderScanning 21 FolderScanWaiting 22 FolderSyncWaiting 23 FolderSyncPreparing 24 FolderSyncing 25 FolderCleaning 26 FolderCleanWaiting 27 FolderError 28) 29 30func (s folderState) String() string { 31 switch s { 32 case FolderIdle: 33 return "idle" 34 case FolderScanning: 35 return "scanning" 36 case FolderScanWaiting: 37 return "scan-waiting" 38 case FolderSyncWaiting: 39 return "sync-waiting" 40 case FolderSyncPreparing: 41 return "sync-preparing" 42 case FolderSyncing: 43 return "syncing" 44 case FolderCleaning: 45 return "cleaning" 46 case FolderCleanWaiting: 47 return "clean-waiting" 48 case FolderError: 49 return "error" 50 default: 51 return "unknown" 52 } 53} 54 55type stateTracker struct { 56 folderID string 57 evLogger events.Logger 58 59 mut sync.Mutex 60 current folderState 61 err error 62 changed time.Time 63} 64 65func newStateTracker(id string, evLogger events.Logger) stateTracker { 66 return stateTracker{ 67 folderID: id, 68 evLogger: evLogger, 69 mut: sync.NewMutex(), 70 } 71} 72 73// setState sets the new folder state, for states other than FolderError. 74func (s *stateTracker) setState(newState folderState) { 75 if newState == FolderError { 76 panic("must use setError") 77 } 78 79 s.mut.Lock() 80 defer s.mut.Unlock() 81 82 if newState == s.current { 83 return 84 } 85 86 /* This should hold later... 87 if s.current != FolderIdle && (newState == FolderScanning || newState == FolderSyncing) { 88 panic("illegal state transition " + s.current.String() + " -> " + newState.String()) 89 } 90 */ 91 92 eventData := map[string]interface{}{ 93 "folder": s.folderID, 94 "to": newState.String(), 95 "from": s.current.String(), 96 } 97 98 if !s.changed.IsZero() { 99 eventData["duration"] = time.Since(s.changed).Seconds() 100 } 101 102 s.current = newState 103 s.changed = time.Now().Truncate(time.Second) 104 105 s.evLogger.Log(events.StateChanged, eventData) 106} 107 108// getState returns the current state, the time when it last changed, and the 109// current error or nil. 110func (s *stateTracker) getState() (current folderState, changed time.Time, err error) { 111 s.mut.Lock() 112 current, changed, err = s.current, s.changed, s.err 113 s.mut.Unlock() 114 return 115} 116 117// setError sets the folder state to FolderError with the specified error or 118// to FolderIdle if the error is nil 119func (s *stateTracker) setError(err error) { 120 s.mut.Lock() 121 defer s.mut.Unlock() 122 123 eventData := map[string]interface{}{ 124 "folder": s.folderID, 125 "from": s.current.String(), 126 } 127 128 if err != nil { 129 eventData["error"] = err.Error() 130 s.current = FolderError 131 } else { 132 s.current = FolderIdle 133 } 134 135 eventData["to"] = s.current.String() 136 137 if !s.changed.IsZero() { 138 eventData["duration"] = time.Since(s.changed).Seconds() 139 } 140 141 s.err = err 142 s.changed = time.Now().Truncate(time.Second) 143 144 s.evLogger.Log(events.StateChanged, eventData) 145} 146