From fe34b08ece37707f80febf7b0267b199a5abe16d Mon Sep 17 00:00:00 2001 From: Lode Hoste Date: Fri, 1 May 2015 14:30:17 +0200 Subject: [PATCH 1/3] Reschedule the next scan interval (fixes #1591) --- cmd/syncthing/gui.go | 9 +++- internal/model/.model.go.swp | Bin 0 -> 16384 bytes internal/model/model.go | 11 +++++ internal/model/rofolder.go | 23 +++++++-- internal/model/rwfolder.go | 49 +++++++++++++------ test/delay_scan_test.go | 91 +++++++++++++++++++++++++++++++++++ test/syncthingprocess.go | 14 ++++++ 7 files changed, 178 insertions(+), 19 deletions(-) create mode 100644 internal/model/.model.go.swp create mode 100644 test/delay_scan_test.go diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 8ce194fda..7e91f9c74 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -155,7 +155,7 @@ func (s *apiSvc) Serve() { postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio) // folder file [perpage] [page] postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores) // folder postRestMux.HandleFunc("/rest/db/override", s.postDBOverride) // folder - postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] + postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] [delay] postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig) // postRestMux.HandleFunc("/rest/system/discovery", s.postSystemDiscovery) // device addr postRestMux.HandleFunc("/rest/system/error", s.postSystemError) // @@ -779,14 +779,21 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { err := s.model.ScanFolderSubs(folder, subs) if err != nil { http.Error(w, err.Error(), 500) + return } } else { errors := s.model.ScanFolders() if len(errors) > 0 { http.Error(w, "Error scanning folders", 500) json.NewEncoder(w).Encode(errors) + return } } + nextStr := qs.Get("next") + next, err := strconv.Atoi(nextStr) + if err == nil { + s.model.DelayScan(folder, time.Duration(next)*time.Second) + } } func (s *apiSvc) postDBPrio(w http.ResponseWriter, r *http.Request) { diff --git a/internal/model/.model.go.swp b/internal/model/.model.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..2ab0c78acd41357e243e6b6bf779d50a48d36227 GIT binary patch literal 16384 zcmeI3ZH!#kS;ucENi%6fD_#;6QFOe>Vb&er*|;u|tZbBc*H#w1#`W4wgV{EB=AN0k z*?aGq+?QQ%UAKr*psH#>tyGov0|>Psgi1&wL;-RV^~zvlbji z{}12)Grs@YzJAY6(CGK|cg)WB)8@bC8klQfu7SA*<{Fr5V6K6=2Id-=YhbQ{xd!GM z_@*=v#X;~&X8wUB!aVv69k_F?*k8lJHYMWYu^?G&x5ame*k|C{t)~QcouvT z{37^y@FDOXa2aG^6I=urz#GAB;JLdQ4-8lacY!ayItV@vYVa_49k>_V1HSmGAox6Z z61;R*5PS+m;O*dT;1qZe90vjTN-GHd9DEi$4L%AC$Uq9-3LXI84PNA(hcAFbO_i44tktwRR)f1}uByqSQZSBQQ z&}t>|UXLA-#L_%2%&^d9c`8dPUZ^;s&gvcB%b0F!_Xk5uVvyvq44Hi5UdZs1sp)T> zwtek1?sZf%KJHD8l}Xaen)J_h&_H{pPAseY*5!4ME5V@7`@)O;Q#epZ`X(PF!|<^p z8jp3+QAw=ezA}B2hO2rz>Fc%Cj*9KKvr(pDBu#W)O@DujF3QB@s;mlLC+P@B>PV`$ zHDAKjlZ)zzT~J*Kx?MFeg&HKOR`SY<+Tnc^Q&%cV(G!zZHPQ;vqm+Ruicv|fqI_5F zM7yC(-x=vbtE5t;&f}8aSYPHRRI{iOCMu(lDd!a!jVKz{m>(;)V{Bvw{e^z2)@4-+ zy+#|VU+AdPZg5#eMa5T@>m6lZg$dOK7_(my)Aqs`Q{tm0Pj_ukCkPgU*2Zp@Ci&K5 zQIU&0%M6d@Rh#gK^S-`vHQ0^{)eeHILCeyCN%{1QNrP4t$2vak{mM zKek?1NDSw)P4c3T+piU5c7jGb@;zv|J!5ZF7L6|**wjt8TS2RE6IkQ=;}R3Dq^8sb z%*e^GD_wu+WDywyZ|p}9v<4Fq4$9#~`@j60mRJr+)HB-};#ON5RaBkVgVKuN1lv4< zeycKc>XZRVStb3FVVofLVtP@C`{Q=>A=%$bhBd^`*9gNgnkJ4qogNdA&PaW|A=A9=SSWuqU*)~tRmZ5~+WX4Ln#xDjJQ z{WKN{+OyindP)0+_BZf`wD)OW%-e+W%3x>-PoJK?GrCS;+!ZWvT{!b~V2zo5#bk4# zoCTh>x=pLtWuejBZYSx!R&0;+Ym?P#-u(N4;{gtB(qz9J(62QD(~e9XIXCOs*f8t1 z_6Qw8kyHMRPil+ZZE2~+iCk} zM^#Ct!wdZAC|wk$z(|+6IwfD*G)y--0;Q1g2En#0qV!drM!Or_*>>zGuKF#5+(~g= z7tHCx$SuSGdZ4TF2tbaHIC1 z;&LP0<|yIFu8BK6rwhAVmgurN-q5qB{8ZE*Dcik<(^Uyxa*P<46HJcA5egmEONO31 zXxS@d`CC@U-ZUX5+zBn|zBws^l3ew6an5YuWno_&>x2i`wu-nA6>3#SX=@`sX>s!h0Lvav*RI$jL8wPl6xhTNlu zxIn@ZuR&wO5#dvdOjeCu`}5+MI0tpRr9!)J3$t(G##JFN>$-TU>whlXDj^rYOTe z^8cD#Qj>E^{@*-*{yTF0FMwYF9|Io&KL*|ft^%nE+zDPFhyPpfCGcC|*TDzD&w^{< z17H>01^$ZM{u%Hq;0m}5cEAO&1pbx${#o!8SO*<&8~75r{xjf{;2Jm!7C`|1nH>Kw zz@LI&1u=L4ybjz0z6;zAK2LuC`{4J$uYr$%ZBT*&MBokJ%jEZe1bz;D0PKPYY=V>E z72sdU{XYl(4EzcBWAMA+x51~uPl6u>Yv5k+0`&o@75pRkGWZnu4e&I$2DU%~M&M!a zJ>Vs32o*3O0&fH7z*(>kzCrEa888BmgL}XoKfs8l^T zAx6+OA<_hLSFUz!KXcQLf)cTaokiL&tS}{&0!*q28>x3Jx)KZnS4MTdW!olEp(AC< z5J^-skt2Ug7(}Bjq7Yyyott_%vbNG%MNC*JN$x;`Fs-(ulp+W9l7xU{M~Wu6k?n*6 z11lsrG&`!qvLUH8qGI08TV+xufdX+)omLVq*-;%ePU}*QB=o7FsZ*VaTT~IWQ$47} zh+3&lA*41?K&hUPmM5kL+gc2^8lPIKdMuNkKp#^>28GE~|X8$F2FoS7{p z3!tHOm91;F(ojk-Y2IG&l^Dk$4kGHMB7B{f^;orlKV8O$*OPz*7Iu6Ua-Zg zhYybzIx6$$uATW(+@jjBwK|w_al=76Ha~*Q#e6F_J9#7T;isVI(krSbEBNt3(v59Zq3pzDw?)hoRa&zCinA$XJ<(J_|^^v}i6UjxBk|H;4qT6t7Rn$8%M5u)g zQHPclDz=oJ+9wRz?UHo%Mx5C3?sA}c$uq}twY?7D>!>=sJ-xoomWA2@F4&2Ni@m+v zFJPana;T|~3+adWIZN9KG+6`P&5FoPa1Ei8Y*A!Kyvd*L64A1!6t`0x3;ua(oDQcV zOpMzzB)ZNW-oPM@nwZy~zu@nDr#du9 zU~@I$Oj!0f6{WFk$kPwfp2YbCe)C_6qu3t?iaGK$g~unjFTAy!hn~jU^8iTO?8pWL z&Yel-)FbQRO!OmiEq9*0#QS!HBjyg5#1VMXEL4wawPWfuhMTg^#087nj|RG8^G3SA zWs^2Xoxfvu4(0KTB!Z=9(VumDEwgZ?E~w5|T$d>I70z}NFRF*slBi*jSd$@UpKX*I zJ$t4Wy}9=O=sKFW=UxQ2CrvgI)gLnAas9&tjK`KNkcE^Y{qq9~d6$xS^ME^95iw8O z+yAKMA=U_H4>%OG(syDt=GOAXI)yc)Fd$0uddD-2EGhTE>%a<5wJt>SQ^Xrmsmgf# z80}nS!O96sHEKg~rS^T5yZZ8n;dK9Ltjwv?PzHrp;v2;!WOu z4?MfJRUM8T9wvITA@1}r>|sVMDN1sot2^98I;KcCY~IX0X?lt$d$pd47o+VcNhS94 vPY|35w(f9$7e-lvxjb&~?`a-^%Drd1oT6IdXZ%sIH5y9!`6X9z-wgf-1Rc0) literal 0 HcmV?d00001 diff --git a/internal/model/model.go b/internal/model/model.go index fc77f5502..a5bb03e84 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -49,6 +49,7 @@ type service interface { Stop() Jobs() ([]string, []string) // In progress, Queued BringToFront(string) + DelayScan(d time.Duration) setState(state folderState) setError(err error) @@ -1322,6 +1323,16 @@ nextSub: return nil } +func (m *Model) DelayScan(folder string, next time.Duration) { + m.fmut.Lock() + runner, ok := m.folderRunners[folder] + m.fmut.Unlock() + if !ok { + return + } + runner.DelayScan(next) +} + // numHashers returns the number of hasher routines to use for a given folder, // taking into account configuration and available CPU cores. func (m *Model) numHashers(folder string) int { diff --git a/internal/model/rofolder.go b/internal/model/rofolder.go index ac867273a..76c928dd8 100644 --- a/internal/model/rofolder.go +++ b/internal/model/rofolder.go @@ -19,6 +19,8 @@ type roFolder struct { folder string intv time.Duration + timer *time.Timer + tmut sync.Mutex // protects timer model *Model stop chan struct{} } @@ -31,6 +33,8 @@ func newROFolder(model *Model, folder string, interval time.Duration) *roFolder }, folder: folder, intv: interval, + timer: time.NewTimer(time.Millisecond), + tmut: sync.NewMutex(), model: model, stop: make(chan struct{}), } @@ -42,13 +46,18 @@ func (s *roFolder) Serve() { defer l.Debugln(s, "exiting") } - timer := time.NewTimer(time.Millisecond) - defer timer.Stop() + defer func() { + s.tmut.Lock() + s.timer.Stop() + s.tmut.Unlock() + }() reschedule := func() { // 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 - timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) + s.tmut.Lock() + s.timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) + s.tmut.Unlock() } initialScanCompleted := false @@ -57,7 +66,7 @@ func (s *roFolder) Serve() { case <-s.stop: return - case <-timer.C: + 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() @@ -105,3 +114,9 @@ func (s *roFolder) BringToFront(string) {} func (s *roFolder) Jobs() ([]string, []string) { return nil, nil } + +func (s *roFolder) DelayScan(next time.Duration) { + s.tmut.Lock() + s.timer.Reset(next) + s.tmut.Unlock() +} diff --git a/internal/model/rwfolder.go b/internal/model/rwfolder.go index fdb7b5ccd..07849483d 100644 --- a/internal/model/rwfolder.go +++ b/internal/model/rwfolder.go @@ -74,6 +74,9 @@ type rwFolder struct { stop chan struct{} queue *jobQueue dbUpdates chan protocol.FileInfo + scanTimer *time.Timer + pullTimer *time.Timer + tmut sync.Mutex // protects scanTimer and pullTimer } func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFolder { @@ -96,8 +99,11 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo shortID: shortID, order: cfg.Order, - stop: make(chan struct{}), - queue: newJobQueue(), + stop: make(chan struct{}), + queue: newJobQueue(), + pullTimer: time.NewTimer(checkPullIntv), + scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately. + tmut: sync.NewMutex(), } } @@ -109,12 +115,11 @@ func (p *rwFolder) Serve() { defer l.Debugln(p, "exiting") } - pullTimer := time.NewTimer(checkPullIntv) - scanTimer := time.NewTimer(time.Millisecond) // The first scan should be done immediately. - defer func() { - pullTimer.Stop() - scanTimer.Stop() + p.tmut.Lock() + p.pullTimer.Stop() + p.scanTimer.Stop() + p.tmut.Unlock() // TODO: Should there be an actual FolderStopped state? p.setState(FolderIdle) }() @@ -135,7 +140,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next rescan in", intv) } - scanTimer.Reset(intv) + p.tmut.Lock() + p.scanTimer.Reset(intv) + p.tmut.Unlock() } // We don't start pulling files until a scan has been completed. @@ -151,12 +158,14 @@ func (p *rwFolder) Serve() { // information is available. Before that though, I'd like to build a // repeatable benchmark of how long it takes to sync a change from // device A to device B, so we have something to work against. - case <-pullTimer.C: + case <-p.pullTimer.C: if !initialScanCompleted { if debug { l.Debugln(p, "skip (initial)") } - pullTimer.Reset(nextPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(nextPullIntv) + p.tmut.Unlock() continue } @@ -180,7 +189,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (curVer == prevVer)", prevVer) } - pullTimer.Reset(checkPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(checkPullIntv) + p.tmut.Unlock() continue } @@ -218,7 +229,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", nextPullIntv) } - pullTimer.Reset(nextPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(nextPullIntv) + p.tmut.Unlock() break } @@ -231,7 +244,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", pauseIntv) } - pullTimer.Reset(pauseIntv) + p.tmut.Lock() + p.pullTimer.Reset(pauseIntv) + p.tmut.Unlock() break } } @@ -240,7 +255,7 @@ func (p *rwFolder) 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 <-scanTimer.C: + case <-p.scanTimer.C: if err := p.model.CheckFolderHealth(p.folder); err != nil { l.Infoln("Skipping folder", p.folder, "scan due to folder error:", err) rescheduleScan() @@ -1165,6 +1180,12 @@ func (p *rwFolder) Jobs() ([]string, []string) { return p.queue.Jobs() } +func (p *rwFolder) DelayScan(next time.Duration) { + p.tmut.Lock() + p.scanTimer.Reset(next) + p.tmut.Unlock() +} + // dbUpdaterRoutine aggregates db updates and commits them in batches no // larger than 1000 items, and no more delayed than 2 seconds. func (p *rwFolder) dbUpdaterRoutine() { diff --git a/test/delay_scan_test.go b/test/delay_scan_test.go new file mode 100644 index 000000000..081100aa6 --- /dev/null +++ b/test/delay_scan_test.go @@ -0,0 +1,91 @@ +// 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/. + +// +build integration + +package integration + +import ( + "io/ioutil" + "log" + "sync" + "testing" + "time" +) + +func TestDelayScan(t *testing.T) { + log.Println("Cleaning...") + err := removeAll("s1", "h1/index*") + if err != nil { + t.Fatal(err) + } + + log.Println("Generating files...") + err = generateFiles("s1", 50, 18, "../LICENSE") + if err != nil { + t.Fatal(err) + } + + log.Println("Generating .stignore...") + err = ioutil.WriteFile("s1/.stignore", []byte("some ignore data\n"), 0644) + if err != nil { + t.Fatal(err) + } + + log.Println("Starting up...") + st := syncthingProcess{ // id1 + instance: "1", + argv: []string{"-home", "h1"}, + port: 8081, + apiKey: apiKey, + } + err = st.start() + if err != nil { + t.Fatal(err) + } + + // Wait for one scan to succeed, or up to 20 seconds... + // This is to let startup, UPnP etc complete. + for i := 0; i < 20; i++ { + err := st.rescan("default") + if err != nil { + time.Sleep(time.Second) + continue + } + break + } + + // Wait for UPnP and stuff + time.Sleep(10 * time.Second) + + var wg sync.WaitGroup + log.Println("Starting scans...") + for j := 0; j < 20; j++ { + j := j + wg.Add(1) + go func() { + defer wg.Done() + err := st.rescanNext("default", time.Duration(1)*time.Second) + log.Println(j) + if err != nil { + log.Println(err) + t.Fatal(err) + } + }() + } + + wg.Wait() + log.Println("Scans done") + time.Sleep(2 * time.Second) + + // This is where the real test is currently, since stop() checks for data + // race output in the log. + log.Println("Stopping...") + _, err = st.stop() + if err != nil { + t.Fatal(err) + } +} diff --git a/test/syncthingprocess.go b/test/syncthingprocess.go index 94e77eaa3..18ccb0c19 100644 --- a/test/syncthingprocess.go +++ b/test/syncthingprocess.go @@ -20,6 +20,7 @@ import ( "net/http" "os" "os/exec" + "strconv" "time" "github.com/syncthing/protocol" @@ -322,6 +323,19 @@ func (p *syncthingProcess) rescan(folder string) error { return nil } +func (p *syncthingProcess) rescanNext(folder string, next time.Duration) error { + resp, err := p.post("/rest/db/scan?folder="+folder+"&next="+strconv.Itoa(int(next.Seconds())), nil) + if err != nil { + return err + } + data, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if resp.StatusCode != 200 { + return fmt.Errorf("Rescan %q: status code %d: %s", folder, resp.StatusCode, data) + } + return nil +} + func (p *syncthingProcess) reset(folder string) error { resp, err := p.post("/rest/system/reset?folder="+folder, nil) if err != nil { From 1bd85d8bafbd11ddc3d4eacfad8bc7576eac0df8 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 3 May 2015 14:18:32 +0200 Subject: [PATCH 2/3] Use a channel instead of locks --- internal/model/rofolder.go | 35 ++++++++++++++++------------------- internal/model/rwfolder.go | 23 ++++++----------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/internal/model/rofolder.go b/internal/model/rofolder.go index 76c928dd8..ae97d3a5a 100644 --- a/internal/model/rofolder.go +++ b/internal/model/rofolder.go @@ -17,12 +17,12 @@ import ( type roFolder struct { stateTracker - folder string - intv time.Duration - timer *time.Timer - tmut sync.Mutex // protects timer - model *Model - stop chan struct{} + folder string + intv time.Duration + timer *time.Timer + model *Model + stop chan struct{} + delayScan chan time.Duration } func newROFolder(model *Model, folder string, interval time.Duration) *roFolder { @@ -31,12 +31,12 @@ func newROFolder(model *Model, folder string, interval time.Duration) *roFolder folder: folder, mut: sync.NewMutex(), }, - folder: folder, - intv: interval, - timer: time.NewTimer(time.Millisecond), - tmut: sync.NewMutex(), - model: model, - stop: make(chan struct{}), + folder: folder, + intv: interval, + timer: time.NewTimer(time.Millisecond), + model: model, + stop: make(chan struct{}), + delayScan: make(chan time.Duration), } } @@ -47,17 +47,13 @@ func (s *roFolder) Serve() { } defer func() { - s.tmut.Lock() s.timer.Stop() - s.tmut.Unlock() }() reschedule := func() { // 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.tmut.Lock() s.timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) - s.tmut.Unlock() } initialScanCompleted := false @@ -97,6 +93,9 @@ func (s *roFolder) Serve() { } reschedule() + + case next := <-s.delayScan: + s.timer.Reset(next) } } } @@ -116,7 +115,5 @@ func (s *roFolder) Jobs() ([]string, []string) { } func (s *roFolder) DelayScan(next time.Duration) { - s.tmut.Lock() - s.timer.Reset(next) - s.tmut.Unlock() + s.delayScan <- next } diff --git a/internal/model/rwfolder.go b/internal/model/rwfolder.go index 07849483d..6fc103399 100644 --- a/internal/model/rwfolder.go +++ b/internal/model/rwfolder.go @@ -76,7 +76,7 @@ type rwFolder struct { dbUpdates chan protocol.FileInfo scanTimer *time.Timer pullTimer *time.Timer - tmut sync.Mutex // protects scanTimer and pullTimer + delayScan chan time.Duration } func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFolder { @@ -103,7 +103,7 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo queue: newJobQueue(), pullTimer: time.NewTimer(checkPullIntv), scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately. - tmut: sync.NewMutex(), + delayScan: make(chan time.Duration), } } @@ -116,10 +116,8 @@ func (p *rwFolder) Serve() { } defer func() { - p.tmut.Lock() p.pullTimer.Stop() p.scanTimer.Stop() - p.tmut.Unlock() // TODO: Should there be an actual FolderStopped state? p.setState(FolderIdle) }() @@ -140,9 +138,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next rescan in", intv) } - p.tmut.Lock() p.scanTimer.Reset(intv) - p.tmut.Unlock() } // We don't start pulling files until a scan has been completed. @@ -163,9 +159,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (initial)") } - p.tmut.Lock() p.pullTimer.Reset(nextPullIntv) - p.tmut.Unlock() continue } @@ -189,9 +183,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (curVer == prevVer)", prevVer) } - p.tmut.Lock() p.pullTimer.Reset(checkPullIntv) - p.tmut.Unlock() continue } @@ -229,9 +221,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", nextPullIntv) } - p.tmut.Lock() p.pullTimer.Reset(nextPullIntv) - p.tmut.Unlock() break } @@ -244,9 +234,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", pauseIntv) } - p.tmut.Lock() p.pullTimer.Reset(pauseIntv) - p.tmut.Unlock() break } } @@ -283,6 +271,9 @@ func (p *rwFolder) Serve() { l.Infoln("Completed initial scan (rw) of folder", p.folder) initialScanCompleted = true } + + case next := <-p.delayScan: + p.scanTimer.Reset(next) } } } @@ -1181,9 +1172,7 @@ func (p *rwFolder) Jobs() ([]string, []string) { } func (p *rwFolder) DelayScan(next time.Duration) { - p.tmut.Lock() - p.scanTimer.Reset(next) - p.tmut.Unlock() + p.delayScan <- next } // dbUpdaterRoutine aggregates db updates and commits them in batches no From bd5a64bac08a707f26fe984a9da742fa4c94fdad Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 3 May 2015 14:18:50 +0200 Subject: [PATCH 3/3] Reschedule before scan --- cmd/syncthing/gui.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 7e91f9c74..a5e046e43 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -775,8 +775,14 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { qs := r.URL.Query() folder := qs.Get("folder") if folder != "" { + nextStr := qs.Get("next") + next, err := strconv.Atoi(nextStr) + if err == nil { + s.model.DelayScan(folder, time.Duration(next)*time.Second) + } + subs := qs["sub"] - err := s.model.ScanFolderSubs(folder, subs) + err = s.model.ScanFolderSubs(folder, subs) if err != nil { http.Error(w, err.Error(), 500) return @@ -789,11 +795,6 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { return } } - nextStr := qs.Get("next") - next, err := strconv.Atoi(nextStr) - if err == nil { - s.model.DelayScan(folder, time.Duration(next)*time.Second) - } } func (s *apiSvc) postDBPrio(w http.ResponseWriter, r *http.Request) {