From dec013044aa0b6ae5a301ffdb63a505d4773aa2a Mon Sep 17 00:00:00 2001 From: slasher Date: Mon, 19 Jan 2026 15:47:28 +0800 Subject: [PATCH] style(common): using generic version of functions . #1000657118 Signed-off-by: slasher --- blobstore/cli/access/cluster.go | 4 +-- blobstore/cli/clustermgr/volumemgr.go | 27 +++++++----------- blobstore/cli/common/cfmt/cluster.go | 6 ++-- blobstore/cli/common/color.go | 38 ++++++-------------------- blobstore/cli/common/color_test.go | 23 ++++++---------- blobstore/util/task/concurrent.go | 36 ++++++++++++++++-------- blobstore/util/task/concurrent_test.go | 34 +++++++++++++++++++---- 7 files changed, 83 insertions(+), 85 deletions(-) diff --git a/blobstore/cli/access/cluster.go b/blobstore/cli/access/cluster.go index f17df1e55..f904f8888 100644 --- a/blobstore/cli/access/cluster.go +++ b/blobstore/cli/access/cluster.go @@ -95,7 +95,7 @@ func showClusters(c *grumble.Context) error { fmt.Println() } fmt.Printf("\tspace in region: %s (%s / %s)\n", region, - common.ColorizeInt64(-available, capacity).Sprint(humanize.IBytes(uint64(available))), + common.ColorizeInteger(-available, capacity).Sprint(humanize.IBytes(uint64(available))), humanize.IBytes(uint64(capacity))) } @@ -126,7 +126,7 @@ func showClusterWithConfig() error { fmt.Println() fmt.Printf("\tspace in cluster: %s (%s / %s)\n", clusterID, - common.ColorizeInt64(-stat.BlobNodeSpaceStat.WritableSpace, stat.BlobNodeSpaceStat.TotalSpace). + common.ColorizeInteger(-stat.BlobNodeSpaceStat.WritableSpace, stat.BlobNodeSpaceStat.TotalSpace). Sprint(humanize.IBytes(uint64(stat.BlobNodeSpaceStat.WritableSpace))), humanize.IBytes(uint64(stat.BlobNodeSpaceStat.TotalSpace))) } diff --git a/blobstore/cli/clustermgr/volumemgr.go b/blobstore/cli/clustermgr/volumemgr.go index 59ac4f638..0e2894fc7 100644 --- a/blobstore/cli/clustermgr/volumemgr.go +++ b/blobstore/cli/clustermgr/volumemgr.go @@ -207,33 +207,26 @@ func cmdListVolumeUnits(c *grumble.Context) error { n := len(volumeInfo.Units) loader := common.Loader(n) - taskArgs := make([]any, n) - for i, arg := range volumeInfo.Units { - taskArgs[i] = arg - } - chunkInfos := make([]string, n) - task.C(func(i int, taskArgs any) { - unit := taskArgs.(clustermgr.Unit) + chunkInfos := task.ConcurrentResult(func(_ int, unit clustermgr.Unit) string { disk, err := cmClient.DiskInfo(ctx, unit.DiskID) if err != nil { - chunkInfos[i] = fmt.Sprintf("ERROR: get disk %d %s", unit.DiskID, err.Error()) loader <- 1 - return + return fmt.Sprintf("ERROR: get disk %d %s", unit.DiskID, err.Error()) } info, err := blobnodeCli.StatChunk(ctx, disk.Host, &blobnode.StatChunkArgs{ DiskID: unit.DiskID, Vuid: unit.Vuid, }) - if err != nil { - chunkInfos[i] = fmt.Sprintf("ERROR: %s %d %s", disk.Host, unit.Vuid, err.Error()) - } else if verbose { - chunkInfos[i] = cfmt.ChunkInfoJoin(info, "\t") - } else { - chunkInfos[i] = fmt.Sprint(info) - } + loader <- 1 - }, taskArgs) + if err != nil { + return fmt.Sprintf("ERROR: %s %d %s", disk.Host, unit.Vuid, err.Error()) + } else if verbose { + return cfmt.ChunkInfoJoin(info, "\t") + } + return fmt.Sprint(info) + }, volumeInfo.Units...) time.Sleep(100 * time.Millisecond) fmt.Println("chunks:") diff --git a/blobstore/cli/common/cfmt/cluster.go b/blobstore/cli/common/cfmt/cluster.go index 76449b0aa..85719159f 100644 --- a/blobstore/cli/common/cfmt/cluster.go +++ b/blobstore/cli/common/cfmt/cluster.go @@ -33,8 +33,8 @@ func VolumeInfoF(vol *clustermgr.VolumeInfo) []string { if vol == nil { return nilStrings[:] } - usedC := common.ColorizeUint64(vol.Used, vol.Total) - freeC := common.ColorizeUint64Free(vol.Free, vol.Total) + usedC := common.ColorizeInteger(vol.Used, vol.Total) + freeC := common.ColorizeIntegerFree(vol.Free, vol.Total) vals := make([]string, 0, 32) vals = append(vals, []string{ fmt.Sprintf("Vid : %d", vol.Vid), @@ -92,7 +92,7 @@ func ClusterInfoF(info *clustermgr.ClusterInfo) []string { if info == nil { return nilStrings[:] } - avaiC := common.ColorizeInt64(-info.Available, info.Capacity) + avaiC := common.ColorizeInteger(-info.Available, info.Capacity) vals := make([]string, 0, 8) vals = append(vals, []string{ fmt.Sprintf("Region : %s", info.Region), diff --git a/blobstore/cli/common/color.go b/blobstore/cli/common/color.go index 97ed1b2e8..20a3efaff 100644 --- a/blobstore/cli/common/color.go +++ b/blobstore/cli/common/color.go @@ -16,6 +16,8 @@ package common import ( "github.com/fatih/color" + + "github.com/cubefs/cubefs/blobstore/util" ) // colorize defined @@ -97,7 +99,7 @@ func Colorize(percent int) *color.Color { } // ColorizeFloat color by float64 [-1.0, 1.0] ratio -func ColorizeFloat(ratio float64) *color.Color { +func ColorizeFloat[T util.Float](ratio T) *color.Color { return Colorize(int(ratio * 100)) } @@ -108,37 +110,13 @@ func percentIfFree(free bool, percent int) int { return percent } -// ColorizeInt by int -func ColorizeInt(used, total int) *color.Color { - return Colorize(percentIfFree(used < 0, used*100/total)) -} - -// ColorizeInt32 by int32 -func ColorizeInt32(used, total int32) *color.Color { +// ColorizeInteger colorize by any integer type +// When used < 0 (signed types only), it indicates free space calculation +func ColorizeInteger[T util.Integer](used, total T) *color.Color { return Colorize(percentIfFree(used < 0, int(used*100/total))) } -// ColorizeInt64 by int64 -func ColorizeInt64(used, total int64) *color.Color { - return Colorize(percentIfFree(used < 0, int(used*100/total))) -} - -// ColorizeUint32 by uint32 -func ColorizeUint32(used, total uint32) *color.Color { - return Colorize(int(used * 100 / total)) -} - -// ColorizeUint64 by uint64 -func ColorizeUint64(used, total uint64) *color.Color { - return Colorize(int(used * 100 / total)) -} - -// ColorizeUint32Free free by uint32 -func ColorizeUint32Free(free, total uint32) *color.Color { - return Colorize(percentIfFree(true, -int(free*100/total))) -} - -// ColorizeUint64Free free by uint64 -func ColorizeUint64Free(free, total uint64) *color.Color { +// ColorizeIntegerFree colorize free space by any integer type +func ColorizeIntegerFree[T util.Integer](free, total T) *color.Color { return Colorize(percentIfFree(true, -int(free*100/total))) } diff --git a/blobstore/cli/common/color_test.go b/blobstore/cli/common/color_test.go index fca8f26ba..8e2d803a4 100644 --- a/blobstore/cli/common/color_test.go +++ b/blobstore/cli/common/color_test.go @@ -17,7 +17,6 @@ package common_test import ( "math/rand" "testing" - "time" "github.com/cubefs/cubefs/blobstore/cli/common" "github.com/cubefs/cubefs/blobstore/cli/common/fmt" @@ -59,7 +58,6 @@ func TestCmdCommonColorRatio(t *testing.T) { {1000, 100, "highlight [Dead]"}, } - rand.Seed(time.Now().Unix()) for _, cs := range cases { for i := 0; i < 10; i++ { percent := rand.Intn(cs.rand) + cs.offset @@ -78,24 +76,19 @@ func TestCmdCommonColorRatio(t *testing.T) { f := rand.Float64() c := common.ColorizeFloat(f) c.Printf("used(%.3f) unknow ", f) - c = common.ColorizeFloat(-f) + c = common.ColorizeFloat(-float32(f)) c.Printf("free(%.3f) unknow ", -f) fmt.Println() } fmt.Println() - common.ColorizeInt(0, 100).Printf("ColorizeInt --> used(%d/%d) white\n", 0, 100) - common.ColorizeInt(-1, 1000).Printf("ColorizeInt --> free(%d/%d) dead", -1, 10000) + common.ColorizeInteger(0, 100).Printf("Int --> used(%d/%d) white\n", 0, 100) + common.ColorizeInteger(-1, 1000).Printf("Int --> free(%d/%d) dead", -1, 10000) fmt.Println() - common.ColorizeInt(80, 100).Printf("ColorizeInt --> used(%d/%d) yellow\n", 80, 100) - common.ColorizeInt(-80, 100).Printf("ColorizeInt --> free(%d/%d) green\n", 80, 100) - common.ColorizeInt32(80, 100).Printf("ColorizeInt32 --> used(%d/%d) yellow\n", 80, 100) - common.ColorizeInt32(-80, 100).Printf("ColorizeInt32 --> free(%d/%d) green\n", 80, 100) - common.ColorizeInt64(80, 100).Printf("ColorizeInt64 --> used(%d/%d) yellow\n", 80, 100) - common.ColorizeInt64(-80, 100).Printf("ColorizeInt64 --> free(%d/%d) green\n", 80, 100) - common.ColorizeUint32(80, 100).Printf("ColorizeUint32 --> used(%d/%d) yellow\n", 80, 100) - common.ColorizeUint32Free(80, 100).Printf("ColorizeUint32Free --> free(%d/%d) green\n", 80, 100) - common.ColorizeUint64(80, 100).Printf("ColorizeUint64 --> used(%d/%d) yellow\n", 80, 100) - common.ColorizeUint64Free(80, 100).Printf("ColorizeUint64Free --> free(%d/%d) green\n", 80, 100) + common.ColorizeInteger(80, 100).Printf("Int --> used(%d/%d) yellow\n", 80, 100) + common.ColorizeInteger(-80, 100).Printf("Int --> free(%d/%d) green\n", 80, 100) + common.ColorizeInteger(int32(80), 100).Printf("Int32 --> used(%d/%d) yellow\n", 80, 100) + common.ColorizeInteger(-80, int32(100)).Printf("Int32 --> free(%d/%d) green\n", 80, 100) + common.ColorizeIntegerFree(uint64(80), 100).Printf("Uint64 --> free(%d/%d) green\n", 80, 100) } diff --git a/blobstore/util/task/concurrent.go b/blobstore/util/task/concurrent.go index e4f97a266..92df23b75 100644 --- a/blobstore/util/task/concurrent.go +++ b/blobstore/util/task/concurrent.go @@ -16,23 +16,15 @@ package task import "context" -var ( - // C alias of Concurrent - C = Concurrent - // CC alias of ConcurrentContext - CC = ConcurrentContext -) - // Concurrent is tasks run concurrently. -func Concurrent(f func(index int, arg interface{}), args []interface{}) { - ConcurrentContext(context.Background(), f, args) +func Concurrent[T any](f func(index int, arg T), args ...T) { + ConcurrentContext(context.Background(), f, args...) } // ConcurrentContext is tasks run concurrently with context. -// How to make []interface{} see: https://golang.org/doc/faq#convert_slice_of_interface -func ConcurrentContext(ctx context.Context, f func(index int, arg interface{}), args []interface{}) { +func ConcurrentContext[T any](ctx context.Context, f func(index int, arg T), args ...T) { tasks := make([]func() error, len(args)) - for ii := 0; ii < len(args); ii++ { + for ii := range args { index, arg := ii, args[ii] tasks[ii] = func() error { f(index, arg) @@ -41,3 +33,23 @@ func ConcurrentContext(ctx context.Context, f func(index int, arg interface{}), } Run(ctx, tasks...) } + +// ConcurrentResult runs tasks concurrently and collects results. +func ConcurrentResult[T, R any](f func(index int, arg T) R, args ...T) []R { + return ConcurrentResultContext(context.Background(), f, args...) +} + +// ConcurrentResultContext runs tasks concurrently with context and collects results. +func ConcurrentResultContext[T, R any](ctx context.Context, f func(index int, arg T) R, args ...T) []R { + results := make([]R, len(args)) + tasks := make([]func() error, len(args)) + for ii := range args { + idx, arg := ii, args[ii] + tasks[ii] = func() error { + results[idx] = f(idx, arg) + return nil + } + } + Run(ctx, tasks...) + return results +} diff --git a/blobstore/util/task/concurrent_test.go b/blobstore/util/task/concurrent_test.go index 39267730a..02f9a7e7c 100644 --- a/blobstore/util/task/concurrent_test.go +++ b/blobstore/util/task/concurrent_test.go @@ -28,11 +28,18 @@ import ( func TestTaskExecuteConcurrent(t *testing.T) { unique := uint32(0) - args := []interface{}{uint32(1), uint32(2), uint32(4)} - task.C(func(index int, arg interface{}) { + task.Concurrent(func(index int, arg uint32) { time.Sleep(time.Millisecond * 100) - atomic.AddUint32(&unique, arg.(uint32)) - }, args) + atomic.AddUint32(&unique, arg) + }, 1, 2, 4) // copy mode, call Concurrent will create args = []uint32{1, 2, 4} + require.Equal(t, uint32(7), atomic.LoadUint32(&unique)) + + atomic.StoreUint32(&unique, 0) + args := []uint32{1, 2, 4} + task.Concurrent(func(index int, arg uint32) { + time.Sleep(time.Millisecond * 100) + atomic.AddUint32(&unique, arg) + }, args...) // reference mode require.Equal(t, uint32(7), atomic.LoadUint32(&unique)) } @@ -40,10 +47,10 @@ func TestTaskExecuteContextCancel(t *testing.T) { unique := uint32(0) ctx, cancel := context.WithCancel(context.Background()) err := task.Run(ctx, func() error { - task.C(func(index int, arg interface{}) { + task.Concurrent(func(index int, arg uint32) { time.Sleep(time.Millisecond * 2000) atomic.AddUint32(&unique, 1) - }, []interface{}{1, 2, 4}) + }, 1, 2, 4) return nil }, func() error { time.Sleep(time.Millisecond * 5000) @@ -55,3 +62,18 @@ func TestTaskExecuteContextCancel(t *testing.T) { require.Contains(t, err.Error(), "canceled") require.Equal(t, uint32(0), atomic.LoadUint32(&unique)) } + +func TestTaskExecuteConcurrentResult(t *testing.T) { + { + results := task.ConcurrentResult(func(index int, arg string) int { + return len(arg) + }) + require.Empty(t, len(results)) + } + { + results := task.ConcurrentResult(func(index int, arg int) int { + return arg * arg + }, 1, 2, 4, 7) + require.Equal(t, []int{1, 4, 16, 49}, results) + } +}