+
+// start starts a sync of the writes so far when none runs and a write is not
+// durable. The caller holds the mutex.
+func (s *Syncer) start() {
+ if s.running || s.durable == s.written || s.failure != nil {
+ return
+ }
+
+ s.running = true
+ s.workers.Add(1)
+
+ go s.run(s.written)
+}
+
+// run syncs once, so the writes up to target become durable, and starts the
+// next sync when writes came during this one.
+func (s *Syncer) run(target uint64) {
+ defer s.workers.Done()
+
+ err := s.sync()
+
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ s.running = false
+ if err != nil {
+ s.failure = fmt.Errorf("sync chat files: %w", err)
+ } else {
+ s.durable = target
+ }
+
+ close(s.done)
+
+ s.done = make(chan struct{})
+ s.start()
+}
+
+// newSyncer returns a syncer that makes the writes durable with sync.
+func newSyncer(sync func() error) *Syncer {
+ return &Syncer{
+ sync: sync,
+ done: make(chan struct{}),
+ }
+}