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
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) {
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)
- }
-
- 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)
-}
-
// Get returns the subagent name with its log open. A name that is not valid or
// that has no directory is an unknown agent.
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 {
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.
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()
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())
-}