mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-08 22:31:04 +00:00
This commit is contained in:
parent
0fe4c01a28
commit
ff2cde469e
@ -313,6 +313,7 @@
|
||||
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="scan-waiting"><span class="hidden-xs" translate>Waiting to scan</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="scanning">
|
||||
<span class="hidden-xs" translate>Scanning</span>
|
||||
|
@ -769,7 +769,7 @@ angular.module('syncthing.core')
|
||||
if (status === 'stopped' || status === 'outofsync' || status === 'error') {
|
||||
return 'danger';
|
||||
}
|
||||
if (status === 'unshared') {
|
||||
if (status === 'unshared' || status === 'scan-waiting') {
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ type OptionsConfiguration struct {
|
||||
TrafficClass int `xml:"trafficClass" json:"trafficClass"`
|
||||
DefaultFolderPath string `xml:"defaultFolderPath" json:"defaultFolderPath" default:"~"`
|
||||
SetLowPriority bool `xml:"setLowPriority" json:"setLowPriority" default:"true"`
|
||||
MaxConcurrentScans int `xml:"maxConcurrentScans" json:"maxConcurrentScans"`
|
||||
|
||||
DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
|
||||
DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes,omitempty" json:"-"`
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
package model
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type byteSemaphore struct {
|
||||
max int
|
||||
@ -25,26 +27,44 @@ func newByteSemaphore(max int) *byteSemaphore {
|
||||
}
|
||||
|
||||
func (s *byteSemaphore) take(bytes int) {
|
||||
s.mut.Lock()
|
||||
if bytes > s.max {
|
||||
bytes = s.max
|
||||
}
|
||||
s.mut.Lock()
|
||||
for bytes > s.available {
|
||||
s.cond.Wait()
|
||||
if bytes > s.max {
|
||||
bytes = s.max
|
||||
}
|
||||
}
|
||||
s.available -= bytes
|
||||
s.mut.Unlock()
|
||||
}
|
||||
|
||||
func (s *byteSemaphore) give(bytes int) {
|
||||
s.mut.Lock()
|
||||
if bytes > s.max {
|
||||
bytes = s.max
|
||||
}
|
||||
s.mut.Lock()
|
||||
if s.available+bytes > s.max {
|
||||
panic("bug: can never give more than max")
|
||||
}
|
||||
s.available = s.max
|
||||
} else {
|
||||
s.available += bytes
|
||||
}
|
||||
s.cond.Broadcast()
|
||||
s.mut.Unlock()
|
||||
}
|
||||
|
||||
func (s *byteSemaphore) setCapacity(cap int) {
|
||||
s.mut.Lock()
|
||||
diff := cap - s.max
|
||||
s.max = cap
|
||||
s.available += diff
|
||||
if s.available < 0 {
|
||||
s.available = 0
|
||||
} else if s.available > s.max {
|
||||
s.available = s.max
|
||||
}
|
||||
s.cond.Broadcast()
|
||||
s.mut.Unlock()
|
||||
}
|
||||
|
113
lib/model/bytesemaphore_test.go
Normal file
113
lib/model/bytesemaphore_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2018 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 "testing"
|
||||
|
||||
func TestZeroByteSempahore(t *testing.T) {
|
||||
// A semaphore with zero capacity is just a no-op.
|
||||
|
||||
s := newByteSemaphore(0)
|
||||
|
||||
// None of these should block or panic
|
||||
s.take(123)
|
||||
s.take(456)
|
||||
s.give(1 << 30)
|
||||
}
|
||||
|
||||
func TestByteSempahoreCapChangeUp(t *testing.T) {
|
||||
// Waiting takes should unblock when the capacity increases
|
||||
|
||||
s := newByteSemaphore(100)
|
||||
|
||||
s.take(75)
|
||||
if s.available != 25 {
|
||||
t.Error("bad state after take")
|
||||
}
|
||||
|
||||
gotit := make(chan struct{})
|
||||
go func() {
|
||||
s.take(75)
|
||||
close(gotit)
|
||||
}()
|
||||
|
||||
s.setCapacity(155)
|
||||
<-gotit
|
||||
if s.available != 5 {
|
||||
t.Error("bad state after both takes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteSempahoreCapChangeDown1(t *testing.T) {
|
||||
// Things should make sense when capacity is adjusted down
|
||||
|
||||
s := newByteSemaphore(100)
|
||||
|
||||
s.take(75)
|
||||
if s.available != 25 {
|
||||
t.Error("bad state after take")
|
||||
}
|
||||
|
||||
s.setCapacity(90)
|
||||
if s.available != 15 {
|
||||
t.Error("bad state after adjust")
|
||||
}
|
||||
|
||||
s.give(75)
|
||||
if s.available != 90 {
|
||||
t.Error("bad state after give")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteSempahoreCapChangeDown2(t *testing.T) {
|
||||
// Things should make sense when capacity is adjusted down, different case
|
||||
|
||||
s := newByteSemaphore(100)
|
||||
|
||||
s.take(75)
|
||||
if s.available != 25 {
|
||||
t.Error("bad state after take")
|
||||
}
|
||||
|
||||
s.setCapacity(10)
|
||||
if s.available != 0 {
|
||||
t.Error("bad state after adjust")
|
||||
}
|
||||
|
||||
s.give(75)
|
||||
if s.available != 10 {
|
||||
t.Error("bad state after give")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteSempahoreGiveMore(t *testing.T) {
|
||||
// We shouldn't end up with more available than we have capacity...
|
||||
|
||||
s := newByteSemaphore(100)
|
||||
|
||||
s.take(150)
|
||||
if s.available != 0 {
|
||||
t.Errorf("bad state after large take")
|
||||
}
|
||||
|
||||
s.give(150)
|
||||
if s.available != 100 {
|
||||
t.Errorf("bad state after large take + give")
|
||||
}
|
||||
|
||||
s.take(150)
|
||||
s.setCapacity(125)
|
||||
// available was zero before, we're increasing capacity by 25
|
||||
if s.available != 25 {
|
||||
t.Errorf("bad state after setcap")
|
||||
}
|
||||
|
||||
s.give(150)
|
||||
if s.available != 125 {
|
||||
t.Errorf("bad state after large take + give with adjustment")
|
||||
}
|
||||
}
|
@ -28,6 +28,9 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/watchaggregator"
|
||||
)
|
||||
|
||||
// scanLimiter limits the number of concurrent scans. A limit of zero means no limit.
|
||||
var scanLimiter = newByteSemaphore(0)
|
||||
|
||||
var errWatchNotStarted = errors.New("not started")
|
||||
|
||||
type folder struct {
|
||||
@ -284,6 +287,10 @@ func (f *folder) scanSubdirs(subDirs []string) error {
|
||||
f.model.fmut.RUnlock()
|
||||
mtimefs := fset.MtimeFS()
|
||||
|
||||
f.setState(FolderScanWaiting)
|
||||
scanLimiter.take(1)
|
||||
defer scanLimiter.give(1)
|
||||
|
||||
for i := range subDirs {
|
||||
sub := osutil.NativeFilename(subDirs[i])
|
||||
|
||||
|
@ -18,6 +18,7 @@ type folderState int
|
||||
const (
|
||||
FolderIdle folderState = iota
|
||||
FolderScanning
|
||||
FolderScanWaiting
|
||||
FolderSyncing
|
||||
FolderError
|
||||
)
|
||||
@ -28,6 +29,8 @@ func (s folderState) String() string {
|
||||
return "idle"
|
||||
case FolderScanning:
|
||||
return "scanning"
|
||||
case FolderScanWaiting:
|
||||
return "scan-waiting"
|
||||
case FolderSyncing:
|
||||
return "syncing"
|
||||
case FolderError:
|
||||
|
@ -170,6 +170,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersi
|
||||
if cfg.Options().ProgressUpdateIntervalS > -1 {
|
||||
go m.progressEmitter.Serve()
|
||||
}
|
||||
scanLimiter.setCapacity(cfg.Options().MaxConcurrentScans)
|
||||
cfg.Subscribe(m)
|
||||
|
||||
return m
|
||||
@ -2586,6 +2587,8 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
}
|
||||
}
|
||||
|
||||
scanLimiter.setCapacity(to.Options.MaxConcurrentScans)
|
||||
|
||||
// Some options don't require restart as those components handle it fine
|
||||
// by themselves. Compare the options structs containing only the
|
||||
// attributes that require restart and act apprioriately.
|
||||
|
Loading…
Reference in New Issue
Block a user