Repository: syncthing/syncthing
File: lib/model/model.go (Go)

Code before:

	m.tearDownFolderLocked(cfg, fmt.Errorf("removing folder %v", cfg.Description()))
	// Remove it from the database
	db.DropFolder(m.db, cfg.ID)
}

// Need to hold lock on m.fmut when calling this.
func (m *model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) {
	// Stop the services running for this folder and wait for them to finish
	// stopping to prevent races on restart.
	tokens := m.folderRunnerTokens[cfg.ID]

	m.fmut.Unlock()

	for _, id := range tokens {
		m.RemoveAndWait(id, 0)
	}

	// Wait for connections to stop to ensure that no more calls to methods
	// expecting this folder to exist happen (e.g. .IndexUpdate).
	m.closeConns(cfg.DeviceIDs(), err).Wait()

	m.fmut.Lock()

	// Clean up our config maps
	delete(m.folderCfgs, cfg.ID)
	delete(m.folderFiles, cfg.ID)
	delete(m.folderIgnores, cfg.ID)
	delete(m.folderRunners, cfg.ID)
	delete(m.folderRunnerTokens, cfg.ID)
}

func (m *model) restartFolder(from, to config.FolderConfiguration) {
	if len(to.ID) == 0 {
		panic("bug: cannot restart empty folder ID")
	}
	if to.ID != from.ID {
		l.Warnf("bug: folder restart cannot change ID %q -> %q", from.ID, to.ID)
		panic("bug: folder restart cannot change ID")
	}

	// This mutex protects the entirety of the restart operation, preventing
	// there from being more than one folder restart operation in progress
	// at any given time. The usual fmut/pmut stuff doesn't cover this,
	// because those locks are released while we are waiting for the folder
	// to shut down (and must be so because the folder might need them as
	// part of its operations before shutting down).
	restartMut := m.folderRestartMuts.Get(to.ID)
	restartMut.Lock()
	defer restartMut.Unlock()


Review comment (suggestion):
Will this not result in a bunch of panics and errors for non-existing folders? We have a bunch of panics in sentry for Index messages for folders that do not exist. If anything, this will make it worse.

Code after:
		// Delete syncthing specific files
		cfg.Filesystem().RemoveAll(config.DefaultMarkerName)
	}

	m.removeFolderLocked(cfg)

	m.fmut.Unlock()

	// Remove it from the database
	db.DropFolder(m.db, cfg.ID)
}

func (m *model) stopFolder(cfg config.FolderConfiguration, err error) {
	// Stop the services running for this folder and wait for them to finish
	// stopping to prevent races on restart.
	m.fmut.RLock()
	tokens := m.folderRunnerTokens[cfg.ID]
	m.fmut.RUnlock()

	for _, id := range tokens {
		m.RemoveAndWait(id, 0)
	}

	// Wait for connections to stop to ensure that no more calls to methods
	// expecting this folder to exist happen (e.g. .IndexUpdate).
	m.closeConns(cfg.DeviceIDs(), err).Wait()
}

// Need to hold lock on m.fmut when calling this.
func (m *model) removeFolderLocked(cfg config.FolderConfiguration) {
	// Clean up our config maps
	delete(m.folderCfgs, cfg.ID)
	delete(m.folderFiles, cfg.ID)
	delete(m.folderIgnores, cfg.ID)
	delete(m.folderRunners, cfg.ID)
	delete(m.folderRunnerTokens, cfg.ID)
}

func (m *model) restartFolder(from, to config.FolderConfiguration) {
	if len(to.ID) == 0 {
		panic("bug: cannot restart empty folder ID")
	}
	if to.ID != from.ID {
		l.Warnf("bug: folder restart cannot change ID %q -> %q", from.ID, to.ID)
		panic("bug: folder restart cannot change ID")
	}

	// This mutex protects the entirety of the restart operation, preventing
	// there from being more than one folder restart operation in progress
	// at any given time. The usual fmut/pmut stuff doesn't cover this,
	// because those locks are released while we are waiting for the folder