X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/5a93b6124068c2a8748fc0bfbb2a2d151815bcf0..2a528fe2981639cf099c713a6be16b8ea0e2cc67:/subagent.go?ds=inline diff --git a/subagent.go b/subagent.go index 4cb6381..9976a4a 100644 --- a/subagent.go +++ b/subagent.go @@ -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()) -}