Skip to content

Commit ee88c60

Browse files
committed
cli/command/container: stats: add snapshot method
Move logic to capture a snapshot of the current stats to the stats struct. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 4c5efd6 commit ee88c60

2 files changed

Lines changed: 30 additions & 16 deletions

File tree

cli/command/container/stats.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -301,16 +301,14 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
301301
}
302302

303303
if options.NoStream {
304-
cStats.mu.RLock()
305-
ccStats := make([]StatsEntry, 0, len(cStats.cs))
306-
for _, c := range cStats.cs {
307-
ccStats = append(ccStats, c.GetStatistics())
308-
}
309-
cStats.mu.RUnlock()
310-
311-
if len(ccStats) == 0 {
304+
statsList := cStats.snapshot()
305+
if len(statsList) == 0 {
312306
return nil
313307
}
308+
ccStats := make([]StatsEntry, 0, len(statsList))
309+
for _, c := range statsList {
310+
ccStats = append(ccStats, c.GetStatistics())
311+
}
314312
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
315313
return err
316314
}
@@ -325,12 +323,16 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
325323
case <-ticker.C:
326324
statsTextBuffer.Reset()
327325
frameBuf.Reset()
328-
cStats.mu.RLock()
329-
ccStats := make([]StatsEntry, 0, len(cStats.cs))
330-
for _, c := range cStats.cs {
326+
statsList := cStats.snapshot()
327+
if len(statsList) == 0 && !showAll {
328+
// Clear screen
329+
_, _ = io.WriteString(dockerCLI.Out(), "\033[H\033[J")
330+
return nil
331+
}
332+
ccStats := make([]StatsEntry, 0, len(statsList))
333+
for _, c := range statsList {
331334
ccStats = append(ccStats, c.GetStatistics())
332335
}
333-
cStats.mu.RUnlock()
334336

335337
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
336338
return err
@@ -347,10 +349,6 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
347349
// We might have fewer containers than before, so let's clear the remaining text
348350
_, _ = fmt.Fprint(&frameBuf, "\033[J")
349351
_, _ = fmt.Fprint(dockerCLI.Out(), frameBuf.String())
350-
351-
if len(ccStats) == 0 && !showAll {
352-
return nil
353-
}
354352
case err, ok := <-closeChan:
355353
if !ok || err == nil || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
356354
// Suppress "unexpected EOF" errors in the CLI so that

cli/command/container/stats_helpers.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
4949
return -1, false
5050
}
5151

52+
// snapshot returns a point-in-time copy of the tracked container list
53+
// (the slice of *Stats pointers). The returned slice is safe for use
54+
// without holding the stats lock, but the underlying Stats values may
55+
// continue to change concurrently.
56+
func (s *stats) snapshot() []*Stats {
57+
s.mu.RLock()
58+
defer s.mu.RUnlock()
59+
if len(s.cs) == 0 {
60+
return nil
61+
}
62+
// https://github.com/golang/go/issues/53643
63+
cp := make([]*Stats, len(s.cs))
64+
copy(cp, s.cs)
65+
return cp
66+
}
67+
5268
func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, streamStats bool, waitFirst *sync.WaitGroup) { //nolint:gocyclo
5369
var getFirst bool
5470

0 commit comments

Comments
 (0)