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

Code before:

	return restoreErrors, nil
}

func (m *model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) ([]Availability, error) {
	m.mut.RLock()
	defer m.mut.RUnlock()

	fs, ok := m.folderFiles[folder]
	cfg := m.folderCfgs[folder]

	if !ok {
		return nil, ErrFolderMissing
	}

	snap, err := fs.Snapshot()
	if err != nil {
		return nil, err
	}
	defer snap.Release()

	if devices := m.availabilityInSnapshot(cfg, snap, file); len(devices) > 0 {
		return devices, nil
	}

	return m.availabilityFromTemporaryRLocked(cfg, file, block), nil
}

func (m *model) availabilityInSnapshot(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability {
	m.mut.RLock()
	defer m.mut.RUnlock()
	return m.availabilityInSnapshotRLocked(cfg, snap, file)
}

func (m *model) availabilityFromTemporaryRLocked(cfg config.FolderConfiguration, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
	var availabilities []Availability
	for _, device := range cfg.Devices {
		if m.deviceDownloads[device.DeviceID].Has(cfg.ID, file.Name, file.Version, int(block.Offset/int64(file.BlockSize()))) {
			availabilities = append(availabilities, Availability{ID: device.DeviceID, FromTemporary: true})
		}
	}
	return availabilities
}

func (m *model) availabilityInSnapshotRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability {
	var availabilities []Availability
	for _, device := range snap.Availability(file.Name) {
		if _, ok := m.remoteFolderStates[device]; !ok {
			continue
		}
		if state := m.remoteFolderStates[device][cfg.ID]; state != remoteFolderValid {

Review comment (suggestion):
You don't actually hold the read lock here. You likely want to acquire it before calling both `availability` functions, and call the `...RLocked` variant of those in both cases.

Code after:

	return restoreErrors, nil
}

func (m *model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) ([]Availability, error) {
	m.mut.RLock()
	defer m.mut.RUnlock()

	fs, ok := m.folderFiles[folder]
	cfg := m.folderCfgs[folder]

	if !ok {
		return nil, ErrFolderMissing
	}

	snap, err := fs.Snapshot()
	if err != nil {
		return nil, err
	}
	defer snap.Release()

	return m.blockAvailabilityRLocked(cfg, snap, file, block), nil
}

func (m *model) blockAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
	m.mut.RLock()
	defer m.mut.RUnlock()
	return m.blockAvailabilityRLocked(cfg, snap, file, block)
}

func (m *model) blockAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
	var candidates []Availability

	candidates = append(candidates, m.fileAvailabilityRLocked(cfg, snap, file)...)
	candidates = append(candidates, m.blockAvailabilityFromTemporaryRLocked(cfg, file, block)...)

	return candidates
}

func (m *model) fileAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability {
	m.mut.RLock()
	defer m.mut.RUnlock()
	return m.fileAvailabilityRLocked(cfg, snap, file)
}

func (m *model) fileAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability {
	var availabilities []Availability
	for _, device := range snap.Availability(file.Name) {
		if _, ok := m.remoteFolderStates[device]; !ok {
			continue
		}