diff --git a/lib/model/folder.go b/lib/model/folder.go index eac3ab058..fa2294c8e 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -50,7 +50,6 @@ type folder struct { scanInterval time.Duration scanTimer *time.Timer - scanNow chan rescanRequest scanDelay chan time.Duration initialScanFinished chan struct{} scanErrors []FileError @@ -58,6 +57,8 @@ type folder struct { pullScheduled chan struct{} + doInSyncChan chan syncRequest + watchCancel context.CancelFunc watchChan chan []string restartWatchChan chan struct{} @@ -67,9 +68,9 @@ type folder struct { puller puller } -type rescanRequest struct { - subdirs []string - err chan error +type syncRequest struct { + fn func() error + err chan error } type puller interface { @@ -90,13 +91,14 @@ func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg conf scanInterval: time.Duration(cfg.RescanIntervalS) * time.Second, scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately. - scanNow: make(chan rescanRequest), scanDelay: make(chan time.Duration), initialScanFinished: make(chan struct{}), scanErrorsMut: sync.NewMutex(), pullScheduled: make(chan struct{}, 1), // This needs to be 1-buffered so that we queue a pull if we're busy when it comes. + doInSyncChan: make(chan syncRequest), + watchCancel: func() {}, restartWatchChan: make(chan struct{}, 1), watchMut: sync.NewMutex(), @@ -172,9 +174,9 @@ func (f *folder) serve(ctx context.Context) { l.Debugln(f, "Scanning due to timer") f.scanTimerFired() - case req := <-f.scanNow: - l.Debugln(f, "Scanning due to request") - req.err <- f.scanSubdirs(req.subdirs) + case req := <-f.doInSyncChan: + l.Debugln(f, "Running something due to request") + req.err <- req.fn() case next := <-f.scanDelay: l.Debugln(f, "Delaying scan") @@ -224,13 +226,19 @@ func (f *folder) Jobs(_, _ int) ([]string, []string, int) { func (f *folder) Scan(subdirs []string) error { <-f.initialScanFinished - req := rescanRequest{ - subdirs: subdirs, - err: make(chan error), + return f.doInSync(func() error { return f.scanSubdirs(subdirs) }) +} + +// doInSync allows to run functions synchronously in folder.serve from exported, +// asynchronously called methods. +func (f *folder) doInSync(fn func() error) error { + req := syncRequest{ + fn: fn, + err: make(chan error, 1), } select { - case f.scanNow <- req: + case f.doInSyncChan <- req: return <-req.err case <-f.ctx.Done(): return f.ctx.Err() diff --git a/lib/model/folder_recvonly.go b/lib/model/folder_recvonly.go index 2973e4c80..81b4068ac 100644 --- a/lib/model/folder_recvonly.go +++ b/lib/model/folder_recvonly.go @@ -64,6 +64,10 @@ func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matche } func (f *receiveOnlyFolder) Revert() { + f.doInSync(func() error { f.revert(); return nil }) +} + +func (f *receiveOnlyFolder) revert() { l.Infof("Reverting folder %v", f.Description) f.setState(FolderScanning) diff --git a/lib/model/folder_sendonly.go b/lib/model/folder_sendonly.go index 9aaabd7f2..18c4ac9c5 100644 --- a/lib/model/folder_sendonly.go +++ b/lib/model/folder_sendonly.go @@ -97,9 +97,15 @@ func (f *sendOnlyFolder) pull() bool { } func (f *sendOnlyFolder) Override() { + f.doInSync(func() error { f.override(); return nil }) +} + +func (f *sendOnlyFolder) override() { l.Infof("Overriding global state on folder %v", f.Description) f.setState(FolderScanning) + defer f.setState(FolderIdle) + batch := make([]protocol.FileInfo, 0, maxBatchSizeFiles) batchSizeBytes := 0 snap := f.fset.Snapshot() @@ -134,5 +140,4 @@ func (f *sendOnlyFolder) Override() { if len(batch) > 0 { f.updateLocalsFromScanning(batch) } - f.setState(FolderIdle) }