]> git.giacomolli.uk - onechat/blobdiff - subagent.go
Exempt tracker metadata from commit signing
[onechat] / subagent.go
index 4cb638191eb1fab90544b016c575d074242187b0..9976a4aaed1023084a236de11f20f6a45300c42c 100644 (file)
@@ -23,19 +23,6 @@ const (
        subagentNameDraws = 1000
 )
 
-// newSubagents returns the subagents in dir, one directory for each name. The
-// clock gives the dates of their messages, and draw gives the names of new
-// subagents.
-func newSubagents(dir string, clock func() time.Time, draw func() string) *Subagents {
-       return &Subagents{
-               dir:      dir,
-               clock:    clock,
-               draw:     draw,
-               open:     make(map[string]*Subagent),
-               reserved: make(map[string]bool),
-       }
-}
-
 // Subagents holds the subagents of one agent. Each subagent has a directory
 // named for it, which holds its log. A log takes an exclusive lock, so
 // Subagents opens the log of a name once and keeps it open until Close. Its
@@ -50,6 +37,30 @@ type Subagents struct {
        reserved map[string]bool
 }
 
+// Subagent is one subagent, its directory, and its log. Its mutex protects the
+// log, because the run of the subagent writes it while the agent reads it with
+// zoom.
+type Subagent struct {
+       name   string
+       dir    string
+       mutex  sync.Mutex
+       log    *Log
+       syncer *Syncer
+}
+
+// newSubagents returns the subagents in dir, one directory for each name. The
+// clock gives the dates of their messages, and draw gives the names of new
+// subagents.
+func newSubagents(dir string, clock func() time.Time, draw func() string) *Subagents {
+       return &Subagents{
+               dir:      dir,
+               clock:    clock,
+               draw:     draw,
+               open:     make(map[string]*Subagent),
+               reserved: make(map[string]bool),
+       }
+}
+
 // Create makes the directory and the log of a new subagent. It draws a name
 // again when the directory of the name exists.
 func (s *Subagents) Create(ctx context.Context) (*Subagent, error) {
@@ -78,43 +89,15 @@ func (s *Subagents) Create(ctx context.Context) (*Subagent, error) {
                        return nil, fmt.Errorf("create subagent directory: %w", err)
                }
 
-               return s.Get(ctx, name)
-       }
-
-       return nil, fmt.Errorf("no free subagent name after %d draws", subagentNameDraws)
-}
-
-// Reserve keeps a child name out of future subagent draws. An existing subagent
-// directory or a reserved name is refused.
-func (s *Subagents) Reserve(name string) error {
-       s.mutex.Lock()
-       defer s.mutex.Unlock()
-
-       if s.reserved[name] {
-               return fmt.Errorf("name %q is already used", name)
-       }
-
-       _, err := os.Lstat(filepath.Join(s.dir, name))
-       if err == nil {
-               return fmt.Errorf("name %q is already used by a subagent", name)
-       }
+               subagent, err := s.Get(ctx, name)
+               if err != nil {
+                       return subagent, fmt.Errorf("open created worker: %w", err)
+               }
 
-       if !errors.Is(err, os.ErrNotExist) {
-               return fmt.Errorf("check subagent name: %w", err)
+               return subagent, nil
        }
 
-       s.reserved[name] = true
-
-       return nil
-}
-
-// Release frees a reservation after a child fails to publish. It does not
-// remove a subagent directory or change an open log.
-func (s *Subagents) Release(name string) {
-       s.mutex.Lock()
-       defer s.mutex.Unlock()
-
-       delete(s.reserved, name)
+       return nil, fmt.Errorf("no free subagent name after %d draws", subagentNameDraws)
 }
 
 // Get returns the subagent name with its log open. A name that is not valid or
@@ -163,11 +146,45 @@ func (s *Subagents) Get(ctx context.Context, name string) (*Subagent, error) {
                log:    log,
                syncer: syncer,
        }
+
        s.open[name] = subagent
 
        return subagent, nil
 }
 
+// Reserve keeps a child name out of future subagent draws. An existing subagent
+// directory or a reserved name is refused.
+func (s *Subagents) Reserve(name string) error {
+       s.mutex.Lock()
+       defer s.mutex.Unlock()
+
+       if s.reserved[name] {
+               return fmt.Errorf("name %q is already used", name)
+       }
+
+       _, err := os.Lstat(filepath.Join(s.dir, name))
+       if err == nil {
+               return fmt.Errorf("name %q is already used by a subagent", name)
+       }
+
+       if !errors.Is(err, os.ErrNotExist) {
+               return fmt.Errorf("check subagent name: %w", err)
+       }
+
+       s.reserved[name] = true
+
+       return nil
+}
+
+// Release frees a reservation after a child fails to publish. It does not
+// remove a subagent directory or change an open log.
+func (s *Subagents) Release(name string) {
+       s.mutex.Lock()
+       defer s.mutex.Unlock()
+
+       delete(s.reserved, name)
+}
+
 // Snapshot flushes and locks the open logs while snapshot reads the subagent
 // files. snapshot must not call Subagents or an open Subagent.
 func (s *Subagents) Snapshot(ctx context.Context, snapshot func() error) error {
@@ -208,15 +225,17 @@ func (s *Subagents) Close() error {
        return err
 }
 
-// Subagent is one subagent, its directory, and its log. Its mutex protects the
-// log, because the run of the subagent writes it while the agent reads it with
-// zoom.
-type Subagent struct {
-       name   string
-       dir    string
-       mutex  sync.Mutex
-       log    *Log
-       syncer *Syncer
+// Flush returns when every write before the call is durable.
+func (a *Subagent) Flush(ctx context.Context) error {
+       return a.syncer.Flush(ctx)
+}
+
+// close makes the writes durable and closes the log.
+func (a *Subagent) close() error {
+       a.mutex.Lock()
+       defer a.mutex.Unlock()
+
+       return errors.Join(a.syncer.Close(), a.log.Close())
 }
 
 // Append logs a message of the subagent.
@@ -234,11 +253,6 @@ func (a *Subagent) Append(kind, text string) (Message, error) {
        return message, nil
 }
 
-// Flush returns when every write before the call is durable.
-func (a *Subagent) Flush(ctx context.Context) error {
-       return a.syncer.Flush(ctx)
-}
-
 // First returns the first message of the log.
 func (a *Subagent) First() (Message, error) {
        a.mutex.Lock()
@@ -271,11 +285,3 @@ func (a *Subagent) Show() (string, error) {
 func (a *Subagent) WorkDir() string {
        return filepath.Join(a.dir, "work")
 }
-
-// close makes the writes durable and closes the log.
-func (a *Subagent) close() error {
-       a.mutex.Lock()
-       defer a.mutex.Unlock()
-
-       return errors.Join(a.syncer.Close(), a.log.Close())
-}