+// dispatch starts the builds that are ready, up to the limit.
+func (m *Memory) dispatch() {
+ for m.ctx.Err() == nil {
+ key, ready := m.queue.Next()
+ if !ready {
+ return
+ }
+
+ task := m.task(key)
+ m.workers.Add(1)
+ go m.run(key, task)
+ }
+}
+
+// task returns the build task of the node key.
+func (m *Memory) task(key nodeKey) Task {
+ task := Task{
+ Level: key.level,
+ Index: key.index,
+ Context: m.context(key.level, key.index),
+ }
+
+ if key.level == 0 {
+ task.Input = []string{leafSource(m.log.Message(key.index))}
+ return task
+ }
+
+ for _, index := range []int{2 * key.index, 2*key.index + 1} {
+ child, _ := m.tree.Node(key.level-1, index)
+ task.Input = append(task.Input, strings.ReplaceAll(child.Text, "\n", " "))
+ }
+
+ return task
+}
+
+// run builds one node with the summarizer. A failed build stays in the queue
+// until the next message.
+func (m *Memory) run(key nodeKey, task Task) {
+ defer m.workers.Done()
+
+ text, err := m.summarizer.Summarize(m.ctx, task)
+
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if err == nil {
+ err = m.build(Node{
+ Level: key.level,
+ Index: key.index,
+ Text: text,
+ Size: len(text),
+ })
+ }
+
+ if err != nil {
+ m.queue.Fail(key)
+ } else {
+ m.queue.Done(key)
+ }
+
+ close(m.changed)
+ m.changed = make(chan struct{})
+ m.dispatch()
+}
+
+// Wait returns when no build runs. The failed builds wait for the next message.
+func (m *Memory) Wait(ctx context.Context) error {
+ for {
+ m.mutex.Lock()
+ running := m.queue.Running()
+ changed := m.changed
+ m.mutex.Unlock()
+
+ if running == 0 {
+ return nil
+ }
+
+ select {
+ case <-changed:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
+