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

Code before:
		case <-s.stop:
			return
		default:
		}
		if s.conn.Closed() {
			// Our work is done.
			return
		}

		// While we have sent a sequence at least equal to the one
		// currently in the database, wait for the local index to update. The
		// local index may update for other folders than the one we are
		// sending for.
		if s.fset.Sequence(protocol.LocalDeviceID) <= s.prevSequence {
			select {
			case <-s.stop:
				return
			case <-evChan:
			case <-time.After(time.Minute):
			}

			continue
		}

		err = s.sendIndexTo()

		// Wait a short amount of time before entering the next loop. If there
		// are continuous changes happening to the local index, this gives us
		// time to batch them up a little.
		time.Sleep(250 * time.Millisecond)
	}
}

func (s *indexSender) Stop() {
	close(s.stop)
	<-s.stopped
}

// Complete always returns true, as Serve only terminates when a connection is
// closed/has failed, in which case retrying doesn't help.
func (s *indexSender) Complete() bool { return true }

// sendIndexTo sends file infos with a sequence number higher than prevSequence and
// returns the highest sent sequence number.
func (s *indexSender) sendIndexTo() error {
	initial := s.prevSequence == 0
	batch := newFileInfoBatch(nil)
	batch.flushFn = func(fs []protocol.FileInfo) error {
		l.Debugf("Sending indexes for %s to %s at %s: %d files (<%d bytes)", s.folder, s.dev, s.conn, len(batch.infos), batch.size)
		if initial {
			initial = false

Review comment (suggestion):
This'll burn a bit more CPU as you'll now loop and create a one minute timer on every event, eventually having potentially hundreds of thousands of timers running in the background. I don't think we need the timer any more either given there's the select on the `stop` in parallel. If we do, a `time.Ticker` declared once outside the loop should do.

Code after:
	defer events.Default.Unsubscribe(sub)

	evChan := sub.C()
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()

	for err == nil {
		select {
		case <-s.stop:
			return
		case <-s.connClosed:
			return
		default:
		}

		// While we have sent a sequence at least equal to the one
		// currently in the database, wait for the local index to update. The
		// local index may update for other folders than the one we are
		// sending for.
		if s.fset.Sequence(protocol.LocalDeviceID) <= s.prevSequence {
			select {
			case <-s.stop:
				return
			case <-s.connClosed:
				return
			case <-evChan:
			case <-ticker.C:
			}

			continue
		}

		err = s.sendIndexTo()

		// Wait a short amount of time before entering the next loop. If there
		// are continuous changes happening to the local index, this gives us
		// time to batch them up a little.
		time.Sleep(250 * time.Millisecond)
	}
}

func (s *indexSender) Stop() {
	close(s.stop)
	<-s.stopped
}

// Complete implements the suture.IsCompletable interface. When Serve terminates
// before Stop is called, the supervisor will check for this method and if it
// returns true removes the service instead of restarting it. Here it always
// returns true, as indexSender only terminates when a connection is
// closed/has failed, in which case retrying doesn't help.