dotcloud/docker

View on GitHub
integration/internal/system/goroutines.go

Summary

Maintainability
A
0 mins
Test Coverage
package system

import (
    "context"
    "time"

    "github.com/docker/docker/client"
    "gotest.tools/v3/poll"
)

// WaitForStableGoroutineCount polls the daemon Info API and returns the reported goroutine count
// after multiple calls return the same number.
func WaitForStableGoroutineCount(ctx context.Context, t poll.TestingT, apiClient client.SystemAPIClient, opts ...poll.SettingOp) int {
    var out int
    // Use a longish delay to make sure the goroutine count is actually stable.
    defaults := []poll.SettingOp{poll.WithTimeout(time.Minute), poll.WithDelay(time.Second)}
    opts = append(defaults, opts...)

    poll.WaitOn(t, StableGoroutineCount(ctx, apiClient, &out), opts...)
    return out
}

// StableGoroutineCount is a [poll.Check] that polls the daemon info API until the goroutine count is the same for 3 iterations.
func StableGoroutineCount(ctx context.Context, apiClient client.SystemAPIClient, count *int) poll.Check {
    var (
        numStable int
        nRoutines int
    )

    return func(t poll.LogT) poll.Result {
        n, err := getGoroutineNumber(ctx, apiClient)
        if err != nil {
            return poll.Error(err)
        }

        last := nRoutines

        if nRoutines == n {
            numStable++
        } else {
            numStable = 0
            nRoutines = n
        }

        if numStable > 3 {
            *count = n
            return poll.Success()
        }
        return poll.Continue("goroutine count is not stable: last %d, current %d, stable iters: %d", last, n, numStable)
    }
}

// CheckGoroutineCount returns a [poll.Check] that polls the daemon info API until the expected number of goroutines is hit.
func CheckGoroutineCount(ctx context.Context, apiClient client.SystemAPIClient, expected int) poll.Check {
    first := true
    return func(t poll.LogT) poll.Result {
        n, err := getGoroutineNumber(ctx, apiClient)
        if err != nil {
            return poll.Error(err)
        }
        if n > expected {
            if first {
                t.Log("Waiting for goroutines to stabilize")
                first = false
            }
            return poll.Continue("exepcted %d goroutines, got %d", expected, n)
        }
        return poll.Success()
    }
}

func getGoroutineNumber(ctx context.Context, apiClient client.SystemAPIClient) (int, error) {
    info, err := apiClient.Info(ctx)
    if err != nil {
        return 0, err
    }
    return info.NGoroutines, nil
}