+59
-14
@@ -134,6 +134,7 @@ type model struct {
|
||||
// folderIOLimiter limits the number of concurrent I/O heavy operations,
|
||||
// such as scans and pulls.
|
||||
folderIOLimiter *byteSemaphore
|
||||
fatalChan chan error
|
||||
|
||||
// fields protected by fmut
|
||||
fmut sync.RWMutex
|
||||
@@ -217,6 +218,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
||||
shortID: id.Short(),
|
||||
globalRequestLimiter: newByteSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
|
||||
folderIOLimiter: newByteSemaphore(cfg.Options().MaxFolderConcurrency()),
|
||||
fatalChan: make(chan error),
|
||||
|
||||
// fields protected by fmut
|
||||
fmut: sync.NewRWMutex(),
|
||||
@@ -253,7 +255,27 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
||||
}
|
||||
|
||||
func (m *model) serve(ctx context.Context) error {
|
||||
// Add and start folders
|
||||
defer m.closeAllConnectionsAndWait()
|
||||
|
||||
m.cfg.Subscribe(m)
|
||||
defer m.cfg.Unsubscribe(m)
|
||||
|
||||
if err := m.initFolders(); err != nil {
|
||||
close(m.started)
|
||||
return util.AsFatalErr(err, util.ExitError)
|
||||
}
|
||||
|
||||
close(m.started)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-m.fatalChan:
|
||||
return util.AsFatalErr(err, util.ExitError)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) initFolders() error {
|
||||
cacheIgnoredFiles := m.cfg.Options().CacheIgnoredFiles
|
||||
existingDevices := m.cfg.Devices()
|
||||
existingFolders := m.cfg.Folders()
|
||||
@@ -263,7 +285,10 @@ func (m *model) serve(ctx context.Context) error {
|
||||
folderCfg.CreateRoot()
|
||||
continue
|
||||
}
|
||||
m.newFolder(folderCfg, cacheIgnoredFiles)
|
||||
err := m.newFolder(folderCfg, cacheIgnoredFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterConfigDevices.add(folderCfg.DeviceIDs())
|
||||
}
|
||||
|
||||
@@ -271,12 +296,10 @@ func (m *model) serve(ctx context.Context) error {
|
||||
m.cleanPending(existingDevices, existingFolders, ignoredDevices, nil)
|
||||
|
||||
m.resendClusterConfig(clusterConfigDevices.AsSlice())
|
||||
m.cfg.Subscribe(m)
|
||||
return nil
|
||||
}
|
||||
|
||||
close(m.started)
|
||||
<-ctx.Done()
|
||||
|
||||
m.cfg.Unsubscribe(m)
|
||||
func (m *model) closeAllConnectionsAndWait() {
|
||||
m.pmut.RLock()
|
||||
closed := make([]chan struct{}, 0, len(m.conn))
|
||||
for id, conn := range m.conn {
|
||||
@@ -287,7 +310,13 @@ func (m *model) serve(ctx context.Context) error {
|
||||
for _, c := range closed {
|
||||
<-c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) fatal(err error) {
|
||||
select {
|
||||
case m.fatalChan <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// StartDeadlockDetector starts a deadlock detector on the models locks which
|
||||
@@ -472,7 +501,7 @@ func (m *model) cleanupFolderLocked(cfg config.FolderConfiguration) {
|
||||
delete(m.folderVersioners, cfg.ID)
|
||||
}
|
||||
|
||||
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) {
|
||||
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) error {
|
||||
if len(to.ID) == 0 {
|
||||
panic("bug: cannot restart empty folder ID")
|
||||
}
|
||||
@@ -512,7 +541,11 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
|
||||
// Create a new fset. Might take a while and we do it under
|
||||
// locking, but it's unsafe to create fset:s concurrently so
|
||||
// that's the price we pay.
|
||||
fset = db.NewFileSet(folder, to.Filesystem(), m.db)
|
||||
var err error
|
||||
fset, err = db.NewFileSet(folder, to.Filesystem(), m.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("restarting %v: %w", to.Description(), err)
|
||||
}
|
||||
}
|
||||
m.addAndStartFolderLocked(to, fset, cacheIgnoredFiles)
|
||||
}
|
||||
@@ -547,12 +580,17 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
|
||||
infoMsg = "Restarted"
|
||||
}
|
||||
l.Infof("%v folder %v (%v)", infoMsg, to.Description(), to.Type)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) {
|
||||
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) error {
|
||||
// Creating the fileset can take a long time (metadata calculation) so
|
||||
// we do it outside of the lock.
|
||||
fset := db.NewFileSet(cfg.ID, cfg.Filesystem(), m.db)
|
||||
fset, err := db.NewFileSet(cfg.ID, cfg.Filesystem(), m.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adding %v: %w", cfg.Description(), err)
|
||||
}
|
||||
|
||||
m.fmut.Lock()
|
||||
defer m.fmut.Unlock()
|
||||
@@ -569,6 +607,7 @@ func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool
|
||||
m.pmut.RUnlock()
|
||||
|
||||
m.addAndStartFolderLocked(cfg, fset, cacheIgnoredFiles)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) UsageReportingStats(report *contract.Report, version int, preview bool) {
|
||||
@@ -2579,7 +2618,10 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
l.Infoln("Paused folder", cfg.Description())
|
||||
} else {
|
||||
l.Infoln("Adding folder", cfg.Description())
|
||||
m.newFolder(cfg, to.Options.CacheIgnoredFiles)
|
||||
if err := m.newFolder(cfg, to.Options.CacheIgnoredFiles); err != nil {
|
||||
m.fatal(err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
clusterConfigDevices.add(cfg.DeviceIDs())
|
||||
}
|
||||
@@ -2603,7 +2645,10 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
// This folder exists on both sides. Settings might have changed.
|
||||
// Check if anything differs that requires a restart.
|
||||
if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) || from.Options.CacheIgnoredFiles != to.Options.CacheIgnoredFiles {
|
||||
m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles)
|
||||
if err := m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles); err != nil {
|
||||
m.fatal(err)
|
||||
return true
|
||||
}
|
||||
clusterConfigDevices.add(fromCfg.DeviceIDs())
|
||||
clusterConfigDevices.add(toCfg.DeviceIDs())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user