func mergedEnumerate(ctx *context.Context, dest chan<- blob.SizedRef, nsrc int, getSource func(int) BlobEnumerator, after string, limit int) error { defer close(dest) subctx := ctx.New() defer subctx.Cancel() startEnum := func(source BlobEnumerator) (*blob.ChanPeeker, <-chan error) { ch := make(chan blob.SizedRef, buffered) errch := make(chan error, 1) go func() { errch <- source.EnumerateBlobs(subctx, ch, after, limit) }() return &blob.ChanPeeker{Ch: ch}, errch } peekers := make([]*blob.ChanPeeker, 0, nsrc) errs := make([]<-chan error, 0, nsrc) for i := 0; i < nsrc; i++ { peeker, errch := startEnum(getSource(i)) peekers = append(peekers, peeker) errs = append(errs, errch) } nSent := 0 var lastSent blob.Ref tooLow := func(br blob.Ref) bool { return lastSent.Valid() && (br == lastSent || br.Less(lastSent)) } for nSent < limit { lowestIdx := -1 var lowest blob.SizedRef for idx, peeker := range peekers { for !peeker.Closed() && tooLow(peeker.MustPeek().Ref) { peeker.Take() } if peeker.Closed() { continue } sb := peeker.MustPeek() // can't be nil if not Closed if lowestIdx == -1 || sb.Ref.Less(lowest.Ref) { lowestIdx = idx lowest = sb } } if lowestIdx == -1 { // all closed break } dest <- lowest nSent++ lastSent = lowest.Ref } // If any part returns an error, we return an error. var retErr error for _, errch := range errs { if err := <-errch; err != nil { retErr = err } } return retErr }
// Collect performs a garbage collection. func (c *Collector) Collect(ctx *context.Context) (err error) { if c.World == nil { return errors.New("no World") } if c.Marker == nil { return errors.New("no Marker") } if c.Roots == nil { return errors.New("no Roots") } if c.Sweeper == nil { return errors.New("no Sweeper") } if c.ItemEnumerator == nil { return errors.New("no ItemEnumerator") } if c.Deleter == nil { return errors.New("no Deleter") } if err := c.World.Stop(); err != nil { return err } defer func() { startErr := c.World.Start() if err == nil { err = startErr } }() // Mark. roots := make(chan Item, buffered) markCtx := ctx.New() var marker syncutil.Group marker.Go(func() error { defer markCtx.Cancel() for it := range roots { if err := c.markItem(markCtx, it, true); err != nil { return err } } return nil }) marker.Go(func() error { return c.Roots.Enumerate(markCtx, roots) }) if err := marker.Err(); err != nil { return fmt.Errorf("Mark failure: %v", err) } // Sweep. all := make(chan Item, buffered) sweepCtx := ctx.New() var sweeper syncutil.Group sweeper.Go(func() error { return c.Sweeper.Enumerate(sweepCtx, all) }) sweeper.Go(func() error { defer sweepCtx.Done() for it := range all { ok, err := c.Marker.IsMarked(it) if err != nil { return err } if !ok { if err := c.Deleter.Delete(it); err != nil { return err } } } return nil }) if err := sweeper.Err(); err != nil { return fmt.Errorf("Sweep failure: %v", err) } return nil }