| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // Copyright (C) 2014 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 http://mozilla.org/MPL/2.0/.
- package model
- import (
- "fmt"
- "math/rand"
- "time"
- "github.com/syncthing/syncthing/lib/sync"
- )
- type roFolder struct {
- stateTracker
- folder string
- intv time.Duration
- timer *time.Timer
- model *Model
- stop chan struct{}
- scanNow chan rescanRequest
- delayScan chan time.Duration
- }
- type rescanRequest struct {
- subs []string
- err chan error
- }
- func newROFolder(model *Model, folder string, interval time.Duration) *roFolder {
- return &roFolder{
- stateTracker: stateTracker{
- folder: folder,
- mut: sync.NewMutex(),
- },
- folder: folder,
- intv: interval,
- timer: time.NewTimer(time.Millisecond),
- model: model,
- stop: make(chan struct{}),
- scanNow: make(chan rescanRequest),
- delayScan: make(chan time.Duration),
- }
- }
- func (s *roFolder) Serve() {
- l.Debugln(s, "starting")
- defer l.Debugln(s, "exiting")
- defer func() {
- s.timer.Stop()
- }()
- reschedule := func() {
- if s.intv == 0 {
- return
- }
- // Sleep a random time between 3/4 and 5/4 of the configured interval.
- sleepNanos := (s.intv.Nanoseconds()*3 + rand.Int63n(2*s.intv.Nanoseconds())) / 4
- s.timer.Reset(time.Duration(sleepNanos) * time.Nanosecond)
- }
- initialScanCompleted := false
- for {
- select {
- case <-s.stop:
- return
- case <-s.timer.C:
- if err := s.model.CheckFolderHealth(s.folder); err != nil {
- l.Infoln("Skipping folder", s.folder, "scan due to folder error:", err)
- reschedule()
- continue
- }
- l.Debugln(s, "rescan")
- if err := s.model.internalScanFolderSubs(s.folder, nil); err != nil {
- // Potentially sets the error twice, once in the scanner just
- // by doing a check, and once here, if the error returned is
- // the same one as returned by CheckFolderHealth, though
- // duplicate set is handled by setError.
- s.setError(err)
- reschedule()
- continue
- }
- if !initialScanCompleted {
- l.Infoln("Completed initial scan (ro) of folder", s.folder)
- initialScanCompleted = true
- }
- if s.intv == 0 {
- continue
- }
- reschedule()
- case req := <-s.scanNow:
- if err := s.model.CheckFolderHealth(s.folder); err != nil {
- l.Infoln("Skipping folder", s.folder, "scan due to folder error:", err)
- req.err <- err
- continue
- }
- l.Debugln(s, "forced rescan")
- if err := s.model.internalScanFolderSubs(s.folder, req.subs); err != nil {
- // Potentially sets the error twice, once in the scanner just
- // by doing a check, and once here, if the error returned is
- // the same one as returned by CheckFolderHealth, though
- // duplicate set is handled by setError.
- s.setError(err)
- req.err <- err
- continue
- }
- req.err <- nil
- case next := <-s.delayScan:
- s.timer.Reset(next)
- }
- }
- }
- func (s *roFolder) Stop() {
- close(s.stop)
- }
- func (s *roFolder) IndexUpdated() {
- }
- func (s *roFolder) Scan(subs []string) error {
- req := rescanRequest{
- subs: subs,
- err: make(chan error),
- }
- s.scanNow <- req
- return <-req.err
- }
- func (s *roFolder) String() string {
- return fmt.Sprintf("roFolder/%s@%p", s.folder, s)
- }
- func (s *roFolder) BringToFront(string) {}
- func (s *roFolder) Jobs() ([]string, []string) {
- return nil, nil
- }
- func (s *roFolder) DelayScan(next time.Duration) {
- s.delayScan <- next
- }
|