mirror of
https://github.com/octoleo/syncthing.git
synced 2025-03-21 10:12:21 +00:00
Tweak locking and integration test.
This commit is contained in:
parent
b67443eb40
commit
d9ffd359e2
@ -18,17 +18,22 @@ func name() string {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var files int
|
var files int
|
||||||
var maxsize int
|
var maxexp int
|
||||||
|
|
||||||
flag.IntVar(&files, "files", 1000, "Number of files")
|
flag.IntVar(&files, "files", 1000, "Number of files")
|
||||||
flag.IntVar(&maxsize, "maxsize", 1000, "Maximum file size (KB)")
|
flag.IntVar(&maxexp, "maxexp", 20, "Maximum file size (max = 2^n + 128*1024 B)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
for i := 0; i < files; i++ {
|
for i := 0; i < files; i++ {
|
||||||
n := name()
|
n := name()
|
||||||
p0 := path.Join(string(n[0]), n[0:2])
|
p0 := path.Join(string(n[0]), n[0:2])
|
||||||
os.MkdirAll(p0, 0755)
|
os.MkdirAll(p0, 0755)
|
||||||
s := mr.Intn(maxsize * 1024)
|
s := 1 << uint(mr.Intn(maxexp))
|
||||||
|
a := 128 * 1024
|
||||||
|
if a > s {
|
||||||
|
a = s
|
||||||
|
}
|
||||||
|
s += mr.Intn(a)
|
||||||
b := make([]byte, s)
|
b := make([]byte, s)
|
||||||
rand.Reader.Read(b)
|
rand.Reader.Read(b)
|
||||||
p1 := path.Join(p0, n)
|
p1 := path.Join(p0, n)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
rm -rf files-* conf-* md5-*
|
rm -rf files-* conf-* md5-*
|
||||||
|
|
||||||
|
extraopts=""
|
||||||
p=$(pwd)
|
p=$(pwd)
|
||||||
|
|
||||||
go build genfiles.go
|
go build genfiles.go
|
||||||
@ -29,19 +30,22 @@ EOT
|
|||||||
|
|
||||||
mkdir files-$i
|
mkdir files-$i
|
||||||
pushd files-$i >/dev/null
|
pushd files-$i >/dev/null
|
||||||
../genfiles -maxsize 780 -files 1500
|
../genfiles -maxexp 21 -files 4000
|
||||||
../md5r > ../md5-$i
|
../md5r > ../md5-$i
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Starting..."
|
echo "Starting..."
|
||||||
for i in 1 2 3 ; do
|
for i in 1 2 3 ; do
|
||||||
syncthing -c conf-$i --no-gui -l :2200$i &
|
sleep 1
|
||||||
|
syncthing -c conf-$i --no-gui -l :2200$i $extraopts &
|
||||||
done
|
done
|
||||||
|
|
||||||
cat md5-* | sort > md5-tot
|
cat md5-* | sort > md5-tot
|
||||||
while true ; do
|
while true ; do
|
||||||
sleep 10
|
read
|
||||||
|
echo Verifying...
|
||||||
|
|
||||||
conv=0
|
conv=0
|
||||||
for i in 1 2 3 ; do
|
for i in 1 2 3 ; do
|
||||||
pushd files-$i >/dev/null
|
pushd files-$i >/dev/null
|
||||||
|
8
main.go
8
main.go
@ -119,6 +119,8 @@ func main() {
|
|||||||
|
|
||||||
myID = string(certId(cert.Certificate[0]))
|
myID = string(certId(cert.Certificate[0]))
|
||||||
infoln("My ID:", myID)
|
infoln("My ID:", myID)
|
||||||
|
log.SetPrefix("[" + myID[0:5] + "] ")
|
||||||
|
logger.SetPrefix("[" + myID[0:5] + "] ")
|
||||||
|
|
||||||
if opts.Debug.Profiler != "" {
|
if opts.Debug.Profiler != "" {
|
||||||
go func() {
|
go func() {
|
||||||
@ -223,7 +225,9 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(opts.Advanced.ScanInterval)
|
time.Sleep(opts.Advanced.ScanInterval)
|
||||||
updateLocalModel(m)
|
if m.LocalAge() > opts.Advanced.ScanInterval.Seconds()/2 {
|
||||||
|
updateLocalModel(m)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -248,7 +252,7 @@ func printStatsLoop(m *model.Model) {
|
|||||||
outbps := 8 * int(float64(stats.OutBytesTotal-lastStats[node].OutBytesTotal)/secs)
|
outbps := 8 * int(float64(stats.OutBytesTotal-lastStats[node].OutBytesTotal)/secs)
|
||||||
|
|
||||||
if inbps+outbps > 0 {
|
if inbps+outbps > 0 {
|
||||||
infof("%s: %sb/s in, %sb/s out", node, MetricPrefix(inbps), MetricPrefix(outbps))
|
infof("%s: %sb/s in, %sb/s out", node[0:5], MetricPrefix(inbps), MetricPrefix(outbps))
|
||||||
}
|
}
|
||||||
|
|
||||||
lastStats[node] = stats
|
lastStats[node] = stats
|
||||||
|
@ -57,6 +57,12 @@ type queuedBlock struct {
|
|||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewFileQueue() *FileQueue {
|
||||||
|
return &FileQueue{
|
||||||
|
availability: make(map[string][]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (q *FileQueue) Add(name string, blocks []Block, monitor Monitor) {
|
func (q *FileQueue) Add(name string, blocks []Block, monitor Monitor) {
|
||||||
q.fmut.Lock()
|
q.fmut.Lock()
|
||||||
defer q.fmut.Unlock()
|
defer q.fmut.Unlock()
|
||||||
@ -212,24 +218,11 @@ func (q *FileQueue) deleteFile(n string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *FileQueue) SetAvailable(file, node string) {
|
func (q *FileQueue) SetAvailable(file string, nodes []string) {
|
||||||
q.amut.Lock()
|
q.amut.Lock()
|
||||||
defer q.amut.Unlock()
|
defer q.amut.Unlock()
|
||||||
|
|
||||||
if q.availability == nil {
|
q.availability[file] = nodes
|
||||||
q.availability = make(map[string][]string)
|
|
||||||
}
|
|
||||||
q.availability[file] = []string{node}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *FileQueue) AddAvailable(file, node string) {
|
|
||||||
q.amut.Lock()
|
|
||||||
defer q.amut.Unlock()
|
|
||||||
|
|
||||||
if q.availability == nil {
|
|
||||||
q.availability = make(map[string][]string)
|
|
||||||
}
|
|
||||||
q.availability[file] = append(q.availability[file], node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *FileQueue) RemoveAvailable(toRemove string) {
|
func (q *FileQueue) RemoveAvailable(toRemove string) {
|
||||||
|
@ -8,14 +8,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFileQueueAdd(t *testing.T) {
|
func TestFileQueueAdd(t *testing.T) {
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.Add("foo", nil, nil)
|
q.Add("foo", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileQueueAddSorting(t *testing.T) {
|
func TestFileQueueAddSorting(t *testing.T) {
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.SetAvailable("zzz", "nodeID")
|
q.SetAvailable("zzz", []string{"nodeID"})
|
||||||
q.SetAvailable("aaa", "nodeID")
|
q.SetAvailable("aaa", []string{"nodeID"})
|
||||||
|
|
||||||
q.Add("zzz", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
q.Add("zzz", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||||
q.Add("aaa", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
q.Add("aaa", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||||
@ -24,9 +24,9 @@ func TestFileQueueAddSorting(t *testing.T) {
|
|||||||
t.Errorf("Incorrectly sorted get: %+v", b)
|
t.Errorf("Incorrectly sorted get: %+v", b)
|
||||||
}
|
}
|
||||||
|
|
||||||
q = FileQueue{}
|
q = NewFileQueue()
|
||||||
q.SetAvailable("zzz", "nodeID")
|
q.SetAvailable("zzz", []string{"nodeID"})
|
||||||
q.SetAvailable("aaa", "nodeID")
|
q.SetAvailable("aaa", []string{"nodeID"})
|
||||||
|
|
||||||
q.Add("zzz", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
q.Add("zzz", []Block{{Offset: 0, Size: 128}, {Offset: 128, Size: 128}}, nil)
|
||||||
b, _ = q.Get("nodeID") // Start on zzzz
|
b, _ = q.Get("nodeID") // Start on zzzz
|
||||||
@ -42,7 +42,7 @@ func TestFileQueueAddSorting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileQueueLen(t *testing.T) {
|
func TestFileQueueLen(t *testing.T) {
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.Add("foo", nil, nil)
|
q.Add("foo", nil, nil)
|
||||||
q.Add("bar", nil, nil)
|
q.Add("bar", nil, nil)
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ func TestFileQueueLen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileQueueGet(t *testing.T) {
|
func TestFileQueueGet(t *testing.T) {
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.SetAvailable("foo", "nodeID")
|
q.SetAvailable("foo", []string{"nodeID"})
|
||||||
q.SetAvailable("bar", "nodeID")
|
q.SetAvailable("bar", []string{"nodeID"})
|
||||||
|
|
||||||
q.Add("foo", []Block{
|
q.Add("foo", []Block{
|
||||||
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
||||||
@ -177,11 +177,9 @@ func TestFileQueueDone(t *testing.T) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func TestFileQueueGetNodeIDs(t *testing.T) {
|
func TestFileQueueGetNodeIDs(t *testing.T) {
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.SetAvailable("a-foo", "nodeID")
|
q.SetAvailable("a-foo", []string{"nodeID", "a"})
|
||||||
q.AddAvailable("a-foo", "a")
|
q.SetAvailable("b-bar", []string{"nodeID", "b"})
|
||||||
q.SetAvailable("b-bar", "nodeID")
|
|
||||||
q.AddAvailable("b-bar", "b")
|
|
||||||
|
|
||||||
q.Add("a-foo", []Block{
|
q.Add("a-foo", []Block{
|
||||||
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
{Offset: 0, Size: 128, Hash: []byte("some foo hash bytes")},
|
||||||
@ -254,9 +252,9 @@ func TestFileQueueThreadHandling(t *testing.T) {
|
|||||||
total += i
|
total += i
|
||||||
}
|
}
|
||||||
|
|
||||||
q := FileQueue{}
|
q := NewFileQueue()
|
||||||
q.Add("foo", blocks, nil)
|
q.Add("foo", blocks, nil)
|
||||||
q.SetAvailable("foo", "nodeID")
|
q.SetAvailable("foo", []string{"nodeID"})
|
||||||
|
|
||||||
var start = make(chan bool)
|
var start = make(chan bool)
|
||||||
var gotTot uint32
|
var gotTot uint32
|
||||||
|
113
model/model.go
113
model/model.go
@ -1,16 +1,5 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Locking
|
|
||||||
=======
|
|
||||||
|
|
||||||
The model has read and write locks. These must be acquired as appropriate by
|
|
||||||
public methods. To prevent deadlock situations, private methods should never
|
|
||||||
acquire locks, but document what locks they require.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"errors"
|
"errors"
|
||||||
@ -40,7 +29,9 @@ type Model struct {
|
|||||||
rawConn map[string]io.Closer
|
rawConn map[string]io.Closer
|
||||||
pmut sync.RWMutex // protects protoConn and rawConn
|
pmut sync.RWMutex // protects protoConn and rawConn
|
||||||
|
|
||||||
fq FileQueue // queue for files to fetch
|
// Queue for files to fetch. fq can call back into the model, so we must ensure
|
||||||
|
// to hold no locks when calling methods on fq.
|
||||||
|
fq *FileQueue
|
||||||
dq chan File // queue for files to delete
|
dq chan File // queue for files to delete
|
||||||
|
|
||||||
updatedLocal int64 // timestamp of last update to local
|
updatedLocal int64 // timestamp of last update to local
|
||||||
@ -100,6 +91,7 @@ func NewModel(dir string) *Model {
|
|||||||
trace: make(map[string]bool),
|
trace: make(map[string]bool),
|
||||||
fileLastChanged: make(map[string]time.Time),
|
fileLastChanged: make(map[string]time.Time),
|
||||||
fileWasSuppressed: make(map[string]int),
|
fileWasSuppressed: make(map[string]int),
|
||||||
|
fq: NewFileQueue(),
|
||||||
dq: make(chan File),
|
dq: make(chan File),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +149,13 @@ func (m *Model) Generation() int64 {
|
|||||||
return m.updatedLocal + m.updateGlobal
|
return m.updatedLocal + m.updateGlobal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) LocalAge() float64 {
|
||||||
|
m.umut.RLock()
|
||||||
|
defer m.umut.RUnlock()
|
||||||
|
|
||||||
|
return time.Since(time.Unix(m.updatedLocal, 0)).Seconds()
|
||||||
|
}
|
||||||
|
|
||||||
type ConnectionInfo struct {
|
type ConnectionInfo struct {
|
||||||
protocol.Statistics
|
protocol.Statistics
|
||||||
Address string
|
Address string
|
||||||
@ -241,9 +240,11 @@ func (m *Model) InSyncSize() (files, bytes int) {
|
|||||||
|
|
||||||
// NeedFiles returns the list of currently needed files and the total size.
|
// NeedFiles returns the list of currently needed files and the total size.
|
||||||
func (m *Model) NeedFiles() (files []File, bytes int) {
|
func (m *Model) NeedFiles() (files []File, bytes int) {
|
||||||
|
qf := m.fq.QueuedFiles()
|
||||||
|
|
||||||
m.gmut.RLock()
|
m.gmut.RLock()
|
||||||
|
|
||||||
for _, n := range m.fq.QueuedFiles() {
|
for _, n := range qf {
|
||||||
f := m.global[n]
|
f := m.global[n]
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
bytes += f.Size()
|
bytes += f.Size()
|
||||||
@ -320,9 +321,11 @@ func (m *Model) indexUpdate(repo map[string]File, f protocol.FileInfo) {
|
|||||||
repo[f.Name] = fileFromFileInfo(f)
|
repo[f.Name] = fileFromFileInfo(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close removes the peer from the model and closes the underlyign connection if possible.
|
// Close removes the peer from the model and closes the underlying connection if possible.
|
||||||
// Implements the protocol.Model interface.
|
// Implements the protocol.Model interface.
|
||||||
func (m *Model) Close(node string, err error) {
|
func (m *Model) Close(node string, err error) {
|
||||||
|
m.fq.RemoveAvailable(node)
|
||||||
|
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
m.rmut.Lock()
|
m.rmut.Lock()
|
||||||
|
|
||||||
@ -334,7 +337,6 @@ func (m *Model) Close(node string, err error) {
|
|||||||
delete(m.remote, node)
|
delete(m.remote, node)
|
||||||
delete(m.protoConn, node)
|
delete(m.protoConn, node)
|
||||||
delete(m.rawConn, node)
|
delete(m.rawConn, node)
|
||||||
m.fq.RemoveAvailable(node)
|
|
||||||
|
|
||||||
m.rmut.Unlock()
|
m.rmut.Unlock()
|
||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
@ -471,37 +473,42 @@ func (m *Model) AddConnection(rawConn io.Closer, protoConn Connection) {
|
|||||||
protoConn.Index(idx)
|
protoConn.Index(idx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if m.rwRunning {
|
m.initmut.Lock()
|
||||||
for i := 0; i < m.parallellRequests; i++ {
|
rw := m.rwRunning
|
||||||
i := i
|
m.initmut.Unlock()
|
||||||
go func() {
|
if !rw {
|
||||||
if m.trace["pull"] {
|
return
|
||||||
log.Println("PULL: Starting", nodeID, i)
|
}
|
||||||
}
|
|
||||||
for {
|
for i := 0; i < m.parallellRequests; i++ {
|
||||||
m.pmut.RLock()
|
i := i
|
||||||
if _, ok := m.protoConn[nodeID]; !ok {
|
go func() {
|
||||||
if m.trace["pull"] {
|
if m.trace["pull"] {
|
||||||
log.Println("PULL: Exiting", nodeID, i)
|
log.Println("PULL: Starting", nodeID, i)
|
||||||
}
|
}
|
||||||
m.pmut.RUnlock()
|
for {
|
||||||
return
|
m.pmut.RLock()
|
||||||
|
if _, ok := m.protoConn[nodeID]; !ok {
|
||||||
|
if m.trace["pull"] {
|
||||||
|
log.Println("PULL: Exiting", nodeID, i)
|
||||||
}
|
}
|
||||||
m.pmut.RUnlock()
|
m.pmut.RUnlock()
|
||||||
|
return
|
||||||
qb, ok := m.fq.Get(nodeID)
|
|
||||||
if ok {
|
|
||||||
if m.trace["pull"] {
|
|
||||||
log.Println("PULL: Request", nodeID, i, qb.name, qb.block.Offset)
|
|
||||||
}
|
|
||||||
data, _ := protoConn.Request(qb.name, qb.block.Offset, qb.block.Size, qb.block.Hash)
|
|
||||||
m.fq.Done(qb.name, qb.block.Offset, data)
|
|
||||||
} else {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
m.pmut.RUnlock()
|
||||||
}
|
|
||||||
|
qb, ok := m.fq.Get(nodeID)
|
||||||
|
if ok {
|
||||||
|
if m.trace["pull"] {
|
||||||
|
log.Println("PULL: Request", nodeID, i, qb.name, qb.block.Offset)
|
||||||
|
}
|
||||||
|
data, _ := protoConn.Request(qb.name, qb.block.Offset, qb.block.Size, qb.block.Hash)
|
||||||
|
m.fq.Done(qb.name, qb.block.Offset, data)
|
||||||
|
} else {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +613,6 @@ func (m *Model) broadcastIndexLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// markDeletedLocals sets the deleted flag on files that have gone missing locally.
|
// markDeletedLocals sets the deleted flag on files that have gone missing locally.
|
||||||
// Must be called with the write lock held.
|
|
||||||
func (m *Model) markDeletedLocals(newLocal map[string]File) bool {
|
func (m *Model) markDeletedLocals(newLocal map[string]File) bool {
|
||||||
// For every file in the existing local table, check if they are also
|
// For every file in the existing local table, check if they are also
|
||||||
// present in the new local table. If they are not, check that we already
|
// present in the new local table. If they are not, check that we already
|
||||||
@ -661,7 +667,6 @@ func (m *Model) updateLocal(f File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called with the write lock held.
|
|
||||||
func (m *Model) recomputeGlobal() {
|
func (m *Model) recomputeGlobal() {
|
||||||
var newGlobal = make(map[string]File)
|
var newGlobal = make(map[string]File)
|
||||||
|
|
||||||
@ -671,23 +676,29 @@ func (m *Model) recomputeGlobal() {
|
|||||||
}
|
}
|
||||||
m.lmut.RUnlock()
|
m.lmut.RUnlock()
|
||||||
|
|
||||||
|
var available = make(map[string][]string)
|
||||||
|
|
||||||
m.rmut.RLock()
|
m.rmut.RLock()
|
||||||
var highestMod int64
|
var highestMod int64
|
||||||
for nodeID, fs := range m.remote {
|
for nodeID, fs := range m.remote {
|
||||||
for n, nf := range fs {
|
for n, nf := range fs {
|
||||||
if lf, ok := newGlobal[n]; !ok || nf.NewerThan(lf) {
|
if lf, ok := newGlobal[n]; !ok || nf.NewerThan(lf) {
|
||||||
newGlobal[n] = nf
|
newGlobal[n] = nf
|
||||||
m.fq.SetAvailable(n, nodeID)
|
available[n] = []string{nodeID}
|
||||||
if nf.Modified > highestMod {
|
if nf.Modified > highestMod {
|
||||||
highestMod = nf.Modified
|
highestMod = nf.Modified
|
||||||
}
|
}
|
||||||
} else if lf.Equals(nf) {
|
} else if lf.Equals(nf) {
|
||||||
m.fq.AddAvailable(n, nodeID)
|
available[n] = append(available[n], nodeID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.rmut.RUnlock()
|
m.rmut.RUnlock()
|
||||||
|
|
||||||
|
for f, ns := range available {
|
||||||
|
m.fq.SetAvailable(f, ns)
|
||||||
|
}
|
||||||
|
|
||||||
// Figure out if anything actually changed
|
// Figure out if anything actually changed
|
||||||
|
|
||||||
m.gmut.RLock()
|
m.gmut.RLock()
|
||||||
@ -727,10 +738,6 @@ func (m *Model) recomputeNeed() {
|
|||||||
m.gmut.RLock()
|
m.gmut.RLock()
|
||||||
|
|
||||||
for n, gf := range m.global {
|
for n, gf := range m.global {
|
||||||
if m.fq.Queued(n) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
m.lmut.RLock()
|
m.lmut.RLock()
|
||||||
lf, ok := m.local[n]
|
lf, ok := m.local[n]
|
||||||
m.lmut.RUnlock()
|
m.lmut.RUnlock()
|
||||||
@ -771,7 +778,9 @@ func (m *Model) recomputeNeed() {
|
|||||||
m.gmut.RUnlock()
|
m.gmut.RUnlock()
|
||||||
|
|
||||||
for _, ao := range toAdd {
|
for _, ao := range toAdd {
|
||||||
m.fq.Add(ao.n, ao.remote, ao.fm)
|
if !m.fq.Queued(ao.n) {
|
||||||
|
m.fq.Add(ao.n, ao.remote, ao.fm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, gf := range toDelete {
|
for _, gf := range toDelete {
|
||||||
m.dq <- gf
|
m.dq <- gf
|
||||||
|
@ -85,6 +85,9 @@ func (m *Model) loadIgnoreFiles(ign map[string][]string) filepath.WalkFunc {
|
|||||||
func (m *Model) walkAndHashFiles(res *[]File, ign map[string][]string) filepath.WalkFunc {
|
func (m *Model) walkAndHashFiles(res *[]File, ign map[string][]string) filepath.WalkFunc {
|
||||||
return func(p string, info os.FileInfo, err error) error {
|
return func(p string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if m.trace["file"] {
|
||||||
|
log.Printf("FILE: %q: %v", p, err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,22 +113,18 @@ func (m *Model) walkAndHashFiles(res *[]File, ign map[string][]string) filepath.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if info.Mode()&os.ModeType == 0 {
|
if info.Mode()&os.ModeType == 0 {
|
||||||
fi, err := os.Stat(p)
|
modified := info.ModTime().Unix()
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
modified := fi.ModTime().Unix()
|
|
||||||
|
|
||||||
m.lmut.RLock()
|
m.lmut.RLock()
|
||||||
hf, ok := m.local[rn]
|
lf, ok := m.local[rn]
|
||||||
m.lmut.RUnlock()
|
m.lmut.RUnlock()
|
||||||
|
|
||||||
if ok && hf.Modified == modified {
|
if ok && lf.Modified == modified {
|
||||||
if nf := uint32(info.Mode()); nf != hf.Flags {
|
if nf := uint32(info.Mode()); nf != lf.Flags {
|
||||||
hf.Flags = nf
|
lf.Flags = nf
|
||||||
hf.Version++
|
lf.Version++
|
||||||
}
|
}
|
||||||
*res = append(*res, hf)
|
*res = append(*res, lf)
|
||||||
} else {
|
} else {
|
||||||
if m.shouldSuppressChange(rn) {
|
if m.shouldSuppressChange(rn) {
|
||||||
if m.trace["file"] {
|
if m.trace["file"] {
|
||||||
@ -133,9 +132,9 @@ func (m *Model) walkAndHashFiles(res *[]File, ign map[string][]string) filepath.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
hf.Flags = protocol.FlagInvalid
|
lf.Flags = protocol.FlagInvalid
|
||||||
hf.Version++
|
lf.Version++
|
||||||
*res = append(*res, hf)
|
*res = append(*res, lf)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -195,9 +194,9 @@ func (m *Model) Walk(followSymlinks bool) (files []File, ignore map[string][]str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fi := range fis {
|
for _, info := range fis {
|
||||||
if fi.Mode()&os.ModeSymlink != 0 {
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
dir := path.Join(m.dir, fi.Name()) + "/"
|
dir := path.Join(m.dir, info.Name()) + "/"
|
||||||
filepath.Walk(dir, m.loadIgnoreFiles(ignore))
|
filepath.Walk(dir, m.loadIgnoreFiles(ignore))
|
||||||
filepath.Walk(dir, hashFiles)
|
filepath.Walk(dir, hashFiles)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user