lib/model: Refactor folderScanner into folder

The functionality was anyway mostly implemented there and not isolated
in the folderScanner type. The attempt to refactor it out in the other
direction wouldn't work given that the event loop and stuff is on
`folder`.
This commit is contained in:
Jakob Borg 2018-05-21 08:45:05 +02:00
parent d64d954721
commit 370a3549e7
2 changed files with 43 additions and 70 deletions

View File

@ -9,6 +9,7 @@ package model
import (
"context"
"errors"
"math/rand"
"time"
"github.com/syncthing/syncthing/lib/config"
@ -18,7 +19,7 @@ import (
"github.com/syncthing/syncthing/lib/watchaggregator"
)
var errWatchNotStarted error = errors.New("not started")
var errWatchNotStarted = errors.New("not started")
type folder struct {
stateTracker
@ -29,7 +30,10 @@ type folder struct {
ctx context.Context
cancel context.CancelFunc
scan folderScanner
scanInterval time.Duration
scanTimer *time.Timer
scanNow chan rescanRequest
scanDelay chan time.Duration
initialScanFinished chan struct{}
pullScheduled chan struct{}
@ -43,6 +47,11 @@ type folder struct {
puller puller
}
type rescanRequest struct {
subdirs []string
err chan error
}
type puller interface {
pull() bool // true when successfull and should not be retried
}
@ -59,7 +68,10 @@ func newFolder(model *Model, cfg config.FolderConfiguration) folder {
ctx: ctx,
cancel: cancel,
scan: newFolderScanner(cfg),
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{}),
pullScheduled: make(chan struct{}, 1), // This needs to be 1-buffered so that we queue a pull if we're busy when it comes.
@ -75,7 +87,7 @@ func (f *folder) Serve() {
defer l.Debugln(f, "exiting")
defer func() {
f.scan.timer.Stop()
f.scanTimer.Stop()
f.setState(FolderIdle)
}()
@ -133,15 +145,15 @@ func (f *folder) Serve() {
// The reason for running the scanner from within the puller is that
// this is the easiest way to make sure we are not doing both at the
// same time.
case <-f.scan.timer.C:
case <-f.scanTimer.C:
l.Debugln(f, "Scanning subdirectories")
f.scanTimerFired()
case req := <-f.scan.now:
case req := <-f.scanNow:
req.err <- f.scanSubdirs(req.subdirs)
case next := <-f.scan.delay:
f.scan.timer.Reset(next)
case next := <-f.scanDelay:
f.scanTimer.Reset(next)
case fsEvents := <-f.watchChan:
l.Debugln(f, "filesystem notification rescan")
@ -156,7 +168,7 @@ func (f *folder) Serve() {
func (f *folder) BringToFront(string) {}
func (f *folder) DelayScan(next time.Duration) {
f.scan.Delay(next)
f.Delay(next)
}
func (f *folder) IgnoresUpdated() {
@ -182,7 +194,27 @@ func (f *folder) Jobs() ([]string, []string) {
func (f *folder) Scan(subdirs []string) error {
<-f.initialScanFinished
return f.scan.Scan(subdirs)
req := rescanRequest{
subdirs: subdirs,
err: make(chan error),
}
f.scanNow <- req
return <-req.err
}
func (f *folder) Reschedule() {
if f.scanInterval == 0 {
return
}
// Sleep a random time between 3/4 and 5/4 of the configured interval.
sleepNanos := (f.scanInterval.Nanoseconds()*3 + rand.Int63n(2*f.scanInterval.Nanoseconds())) / 4
interval := time.Duration(sleepNanos) * time.Nanosecond
l.Debugln(f, "next rescan in", interval)
f.scanTimer.Reset(interval)
}
func (f *folder) Delay(next time.Duration) {
f.scanDelay <- next
}
func (f *folder) Stop() {
@ -242,7 +274,7 @@ func (f *folder) scanTimerFired() {
close(f.initialScanFinished)
}
f.scan.Reschedule()
f.Reschedule()
}
func (f *folder) WatchError() error {

View File

@ -1,59 +0,0 @@
// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package model
import (
"github.com/syncthing/syncthing/lib/config"
"math/rand"
"time"
)
type rescanRequest struct {
subdirs []string
err chan error
}
// bundle all folder scan activity
type folderScanner struct {
interval time.Duration
timer *time.Timer
now chan rescanRequest
delay chan time.Duration
}
func newFolderScanner(config config.FolderConfiguration) folderScanner {
return folderScanner{
interval: time.Duration(config.RescanIntervalS) * time.Second,
timer: time.NewTimer(time.Millisecond), // The first scan should be done immediately.
now: make(chan rescanRequest),
delay: make(chan time.Duration),
}
}
func (f *folderScanner) Reschedule() {
if f.interval == 0 {
return
}
// Sleep a random time between 3/4 and 5/4 of the configured interval.
sleepNanos := (f.interval.Nanoseconds()*3 + rand.Int63n(2*f.interval.Nanoseconds())) / 4
interval := time.Duration(sleepNanos) * time.Nanosecond
l.Debugln(f, "next rescan in", interval)
f.timer.Reset(interval)
}
func (f *folderScanner) Scan(subdirs []string) error {
req := rescanRequest{
subdirs: subdirs,
err: make(chan error),
}
f.now <- req
return <-req.err
}
func (f *folderScanner) Delay(next time.Duration) {
f.delay <- next
}