Repository: moby/moby
File: daemon/list.go (Go)

Code before:
	// shortcut if there are no containers
	if numContainers == 0 {
		return []*containertypes.Summary{}, nil
	}

	// Get the info for each container in the list; this can be slow so we
	// dispatch a set number of worker goroutines to do the jobs. We choose
	// log2(numContainers) workers to avoid creating too many goroutines
	// for large number of containers.
	numWorkers := int(math.Log2(float64(numContainers)))
	if numWorkers < 1 {
		numWorkers = 1
	}

	resultsMut := sync.Mutex{}
	results := []*containertypes.Summary{}

	// Pre-fill results slice so we can later insert at specific indexes
	for range numContainers {
		results = append(results, nil)
	}

	g, ctx := errgroup.WithContext(ctx)
	g.SetLimit(numWorkers)

	for _, ctr := range containerList {
		stop := false
		switch includeContainerInList(&ctr, filter) {
		case excludeContainer:
			continue
		case stopIteration:
			stop = true
		case includeContainer:
			idx := filter.idx
			filter.idx++

			g.Go(func() error {
				// refresh the container image info (in case the image changed in
				// the repository)
				newC := daemon.refreshImage(ctx, &ctr)

				// get the image size (calculation is slow)
				if filter.Size {
					var err error
					newC.SizeRw, newC.SizeRootFs, err = daemon.imageService.GetContainerLayerSize(ctx, newC.ID)
					if err != nil {
						return err
					}
				}

				// insert the result at the given index (so the output is in the

Review comment (suggestion):
`containerList` is a slice of `container.Snapshot` (not `*container.Snapshot`), so modifying the `ctr` inside the loop (by `refreshImage`) will ONLY affect the locally-scoped `ctr` inside the for loop, and not the value in the `containerList` itself.

```suggestion
	for i := range containerList {
		ctr := &containerList[i]
```

Code after:
	// shortcut if there are no containers
	if numContainers == 0 {
		return []*containertypes.Summary{}, nil
	}

	// Get the info for each container in the list; this can be slow so we
	// dispatch a set number of worker goroutines to do the jobs. We choose
	// log2(numContainers) workers to avoid creating too many goroutines
	// for large number of containers.
	numWorkers := int(math.Log2(float64(numContainers)))
	if numWorkers < 1 {
		numWorkers = 1
	}

	resultsMut := sync.Mutex{}
	results := make([]*containertypes.Summary, numContainers)

	g, ctx := errgroup.WithContext(ctx)
	g.SetLimit(numWorkers)

	for i := range containerList {
		currentContainer := &containerList[i]
		stop := false
		switch includeContainerInList(currentContainer, filter) {
		case excludeContainer:
			continue
		case stopIteration:
			stop = true
		case includeContainer:
			idx := filter.idx
			filter.idx++

			g.Go(func() error {
				// refresh the container image info (in case the image changed in
				// the repository)
				newC := daemon.refreshImage(ctx, currentContainer)

				// get the image size (calculation is slow)
				if filter.Size {
					var err error
					newC.SizeRw, newC.SizeRootFs, err = daemon.imageService.GetContainerLayerSize(ctx, newC.ID)
					if err != nil {
						return err
					}
				}

				// insert the result at the given index (so the output is in the
				// same order as containerList above).
				resultsMut.Lock()
				results[idx] = newC
				resultsMut.Unlock()