]> git.giacomolli.uk - onechat/blobdiff - syncer.go
Order model and chat declarations
[onechat] / syncer.go
index 6b69f860c59c18cb615d40b0ed37e53f39b299b2..0784393fcd4df9bbfc44e8d99877eaca9f3bac71 100644 (file)
--- a/syncer.go
+++ b/syncer.go
@@ -10,17 +10,17 @@ import (
        "golang.org/x/sys/unix"
 )
 
        "golang.org/x/sys/unix"
 )
 
-// syncer makes the writes of a chat durable by group commit. Each write goes to
+// Syncer makes the writes of a chat durable by group commit. Each write goes to
 // the operating system at once, so a crash of the process loses nothing. After
 // each write, Written starts a background sync of the file system of the chat,
 // unless one runs already. Then the next sync starts when the running one ends.
 // One sync makes every write before its start durable, so a sync covers many
 // the operating system at once, so a crash of the process loses nothing. After
 // each write, Written starts a background sync of the file system of the chat,
 // unless one runs already. Then the next sync starts when the running one ends.
 // One sync makes every write before its start durable, so a sync covers many
-// writes, and there is never more than one sync at a time. A power loss loses at
-// most the writes after the start of the last finished sync.
+// writes, and there is never more than one sync at a time. A power loss loses
+// at most the writes after the start of the last finished sync.
 //
 // A failed sync can mean that the kernel dropped written data, so the error
 // stays, and each later Flush returns it.
 //
 // A failed sync can mean that the kernel dropped written data, so the error
 // stays, and each later Flush returns it.
-type syncer struct {
+type Syncer struct {
        mutex   sync.Mutex
        sync    func() error
        file    *os.File
        mutex   sync.Mutex
        sync    func() error
        file    *os.File
@@ -34,7 +34,7 @@ type syncer struct {
 
 // openSyncer returns the syncer of the file system that holds dir. It syncs
 // with syncfs, which writes back only that file system.
 
 // openSyncer returns the syncer of the file system that holds dir. It syncs
 // with syncfs, which writes back only that file system.
-func openSyncer(dir string) (*syncer, error) {
+func openSyncer(dir string) (*Syncer, error) {
        file, err := os.Open(dir)
        if err != nil {
                return nil, fmt.Errorf("open chat directory: %w", err)
        file, err := os.Open(dir)
        if err != nil {
                return nil, fmt.Errorf("open chat directory: %w", err)
@@ -49,8 +49,8 @@ func openSyncer(dir string) (*syncer, error) {
 }
 
 // newSyncer returns a syncer that makes the writes durable with sync.
 }
 
 // newSyncer returns a syncer that makes the writes durable with sync.
-func newSyncer(sync func() error) *syncer {
-       return &syncer{
+func newSyncer(sync func() error) *Syncer {
+       return &Syncer{
                sync: sync,
                done: make(chan struct{}),
        }
                sync: sync,
                done: make(chan struct{}),
        }
@@ -58,7 +58,7 @@ func newSyncer(sync func() error) *syncer {
 
 // Written tells the syncer that a write went to the operating system, and
 // starts a sync when none runs.
 
 // Written tells the syncer that a write went to the operating system, and
 // starts a sync when none runs.
-func (s *syncer) Written() {
+func (s *Syncer) Written() {
        s.mutex.Lock()
        defer s.mutex.Unlock()
 
        s.mutex.Lock()
        defer s.mutex.Unlock()
 
@@ -66,48 +66,16 @@ func (s *syncer) Written() {
        s.start()
 }
 
        s.start()
 }
 
-// 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()
-}
-
 // Flush returns when every write before the call is durable.
 // Flush returns when every write before the call is durable.
-func (s *syncer) Flush(ctx context.Context) error {
+func (s *Syncer) Flush(ctx context.Context) error {
        s.mutex.Lock()
        s.mutex.Lock()
+
        target := s.written
        s.mutex.Unlock()
 
        for {
                s.mutex.Lock()
        target := s.written
        s.mutex.Unlock()
 
        for {
                s.mutex.Lock()
+
                reached := s.durable >= target
                failure := s.failure
                done := s.done
                reached := s.durable >= target
                failure := s.failure
                done := s.done
@@ -131,9 +99,10 @@ func (s *syncer) Flush(ctx context.Context) error {
 
 // Close makes every write durable, waits for the syncs, and closes the
 // directory.
 
 // Close makes every write durable, waits for the syncs, and closes the
 // directory.
-func (s *syncer) Close() error {
+func (s *Syncer) Close() error {
        err := s.Flush(context.Background())
        s.workers.Wait()
        err := s.Flush(context.Background())
        s.workers.Wait()
+
        if s.file != nil {
                err = errors.Join(err, s.file.Close())
                s.file = nil
        if s.file != nil {
                err = errors.Join(err, s.file.Close())
                s.file = nil
@@ -141,3 +110,39 @@ func (s *syncer) Close() error {
 
        return err
 }
 
        return err
 }
+
+// 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()
+}