mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-10 15:20:56 +00:00
Deadlock fix and cleanups
This commit is contained in:
parent
f0b18685a5
commit
ba0e4ded65
@ -6,7 +6,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = log.New(os.Stderr, "", log.Ltime)
|
// set in main()
|
||||||
|
var logger *log.Logger
|
||||||
|
|
||||||
func debugln(vals ...interface{}) {
|
func debugln(vals ...interface{}) {
|
||||||
s := fmt.Sprintln(vals...)
|
s := fmt.Sprintln(vals...)
|
||||||
|
5
main.go
5
main.go
@ -97,8 +97,11 @@ func main() {
|
|||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
logger = log.New(os.Stderr, "", log.Flags())
|
||||||
if len(opts.Debug.TraceModel) > 0 || opts.Debug.LogSource {
|
if len(opts.Debug.TraceModel) > 0 || opts.Debug.LogSource {
|
||||||
logger = log.New(os.Stderr, "", log.Lshortfile|log.Ldate|log.Ltime|log.Lmicroseconds)
|
log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
|
logger.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
}
|
}
|
||||||
opts.ConfDir = expandTilde(opts.ConfDir)
|
opts.ConfDir = expandTilde(opts.ConfDir)
|
||||||
|
|
||||||
|
@ -29,7 +29,10 @@ func (m *fileMonitor) FileBegins(cc <-chan content) error {
|
|||||||
dir := path.Dir(tmp)
|
dir := path.Dir(tmp)
|
||||||
_, err := os.Stat(dir)
|
_, err := os.Stat(dir)
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
os.MkdirAll(dir, 0777)
|
err = os.MkdirAll(dir, 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outFile, err := os.Create(tmp)
|
outFile, err := os.Create(tmp)
|
||||||
@ -106,6 +109,13 @@ func (m *fileMonitor) FileDone() error {
|
|||||||
tmp := tempName(m.path, m.global.Modified)
|
tmp := tempName(m.path, m.global.Modified)
|
||||||
defer os.Remove(tmp)
|
defer os.Remove(tmp)
|
||||||
|
|
||||||
|
if m.copyError != nil {
|
||||||
|
return m.copyError
|
||||||
|
}
|
||||||
|
if m.writeError != nil {
|
||||||
|
return m.writeError
|
||||||
|
}
|
||||||
|
|
||||||
err := hashCheck(tmp, m.global.Blocks)
|
err := hashCheck(tmp, m.global.Blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s (tmp) (deleting)", path.Base(m.name), err.Error())
|
return fmt.Errorf("%s: %s (tmp) (deleting)", path.Base(m.name), err.Error())
|
||||||
|
@ -7,20 +7,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resolver interface {
|
|
||||||
WhoHas(string) []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Monitor interface {
|
type Monitor interface {
|
||||||
FileBegins(<-chan content) error
|
FileBegins(<-chan content) error
|
||||||
FileDone() error
|
FileDone() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileQueue struct {
|
type FileQueue struct {
|
||||||
resolver Resolver
|
|
||||||
files queuedFileList
|
files queuedFileList
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
sorted bool
|
sorted bool
|
||||||
|
availability map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type queuedFile struct {
|
type queuedFile struct {
|
||||||
@ -92,32 +88,28 @@ func (q *FileQueue) Get(nodeID string) (queuedBlock, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range q.files {
|
for i := range q.files {
|
||||||
if time.Since(q.files[i].nodesChecked) > 5*time.Second {
|
qf := &q.files[i]
|
||||||
// Refresh node list every now and then
|
|
||||||
q.files[i].nodes = q.resolver.WhoHas(q.files[i].name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(q.files[i].nodes) == 0 {
|
if len(q.availability[qf.name]) == 0 {
|
||||||
// Noone has the file we want; abort.
|
// Noone has the file we want; abort.
|
||||||
if q.files[i].remaining != len(q.files[i].blocks) {
|
if qf.remaining != len(qf.blocks) {
|
||||||
// We have already started on this file; close it down
|
// We have already started on this file; close it down
|
||||||
close(q.files[i].channel)
|
close(qf.channel)
|
||||||
if mon := q.files[i].monitor; mon != nil {
|
if mon := qf.monitor; mon != nil {
|
||||||
mon.FileDone()
|
mon.FileDone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q.deleteIndex(i)
|
q.deleteAt(i)
|
||||||
return queuedBlock{}, false
|
return queuedBlock{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
qf := q.files[i]
|
for _, ni := range q.availability[qf.name] {
|
||||||
for _, ni := range qf.nodes {
|
|
||||||
// Find and return the next block in the queue
|
// Find and return the next block in the queue
|
||||||
if ni == nodeID {
|
if ni == nodeID {
|
||||||
for j, b := range qf.blocks {
|
for j, b := range qf.blocks {
|
||||||
if !qf.activeBlocks[j] {
|
if !qf.activeBlocks[j] {
|
||||||
q.files[i].activeBlocks[j] = true
|
qf.activeBlocks[j] = true
|
||||||
q.files[i].given++
|
qf.given++
|
||||||
return queuedBlock{
|
return queuedBlock{
|
||||||
name: qf.name,
|
name: qf.name,
|
||||||
block: b,
|
block: b,
|
||||||
@ -142,29 +134,31 @@ func (q *FileQueue) Done(file string, offset int64, data []byte) {
|
|||||||
offset: offset,
|
offset: offset,
|
||||||
data: data,
|
data: data,
|
||||||
}
|
}
|
||||||
for i, qf := range q.files {
|
for i := range q.files {
|
||||||
|
qf := &q.files[i]
|
||||||
|
|
||||||
if qf.name == file {
|
if qf.name == file {
|
||||||
if qf.monitor != nil && qf.remaining == len(qf.blocks) {
|
if qf.monitor != nil && qf.remaining == len(qf.blocks) {
|
||||||
err := qf.monitor.FileBegins(qf.channel)
|
err := qf.monitor.FileBegins(qf.channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WARNING: %s: %v (not synced)", qf.name, err)
|
log.Printf("WARNING: %s: %v (not synced)", qf.name, err)
|
||||||
q.deleteIndex(i)
|
q.deleteAt(i)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qf.channel <- c
|
qf.channel <- c
|
||||||
q.files[i].remaining--
|
qf.remaining--
|
||||||
|
|
||||||
if q.files[i].remaining == 0 {
|
if qf.remaining == 0 {
|
||||||
close(qf.channel)
|
close(qf.channel)
|
||||||
q.deleteIndex(i)
|
|
||||||
if qf.monitor != nil {
|
if qf.monitor != nil {
|
||||||
err := qf.monitor.FileDone()
|
err := qf.monitor.FileDone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WARNING: %s: %v", qf.name, err)
|
log.Printf("WARNING: %s: %v", qf.name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
q.deleteAt(i)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -194,6 +188,24 @@ func (q *FileQueue) QueuedFiles() (files []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *FileQueue) deleteIndex(i int) {
|
func (q *FileQueue) deleteAt(i int) {
|
||||||
q.files = q.files[:i+copy(q.files[i:], q.files[i+1:])]
|
q.files = q.files[:i+copy(q.files[i:], q.files[i+1:])]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *FileQueue) SetAvailable(file, node string) {
|
||||||
|
q.lock.Lock()
|
||||||
|
defer q.lock.Unlock()
|
||||||
|
if q.availability == nil {
|
||||||
|
q.availability = make(map[string][]string)
|
||||||
|
}
|
||||||
|
q.availability[file] = []string{node}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *FileQueue) AddAvailable(file, node string) {
|
||||||
|
q.lock.Lock()
|
||||||
|
defer q.lock.Unlock()
|
||||||
|
if q.availability == nil {
|
||||||
|
q.availability = make(map[string][]string)
|
||||||
|
}
|
||||||
|
q.availability[file] = append(q.availability[file], node)
|
||||||
|
}
|
||||||
|
@ -94,7 +94,6 @@ func NewModel(dir string) *Model {
|
|||||||
fileWasSuppressed: make(map[string]int),
|
fileWasSuppressed: make(map[string]int),
|
||||||
dq: make(chan File),
|
dq: make(chan File),
|
||||||
}
|
}
|
||||||
m.fq.resolver = m
|
|
||||||
|
|
||||||
go m.broadcastIndexLoop()
|
go m.broadcastIndexLoop()
|
||||||
return m
|
return m
|
||||||
@ -618,13 +617,16 @@ func (m *Model) recomputeGlobal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var highestMod int64
|
var highestMod int64
|
||||||
for _, 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)
|
||||||
if nf.Modified > highestMod {
|
if nf.Modified > highestMod {
|
||||||
highestMod = nf.Modified
|
highestMod = nf.Modified
|
||||||
}
|
}
|
||||||
|
} else if lf.Equals(nf) {
|
||||||
|
m.fq.AddAvailable(n, nodeID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,8 +692,14 @@ func (m *Model) recomputeNeed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called with the read lock held.
|
|
||||||
func (m *Model) WhoHas(name string) []string {
|
func (m *Model) WhoHas(name string) []string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
return m.whoHas(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be called with the read lock held.
|
||||||
|
func (m *Model) whoHas(name string) []string {
|
||||||
var remote []string
|
var remote []string
|
||||||
|
|
||||||
gf := m.global[name]
|
gf := m.global[name]
|
||||||
|
Loading…
Reference in New Issue
Block a user