cmd/go/internal/par: generic Work

This makes the mvs code slightly clearer.

Change-Id: Idefc36bd1066f0348a70e7c91c37a0d56f3c02d5
Reviewed-on: https://go-review.googlesource.com/c/go/+/463844
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: roger peppe <rogpeppe@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go
index eb33ebd..ec5e49a 100644
--- a/src/cmd/go/internal/mvs/mvs.go
+++ b/src/cmd/go/internal/mvs/mvs.go
@@ -110,12 +110,11 @@
 
 	// Explore work graph in parallel in case reqs.Required
 	// does high-latency network operations.
-	var work par.Work
+	var work par.Work[module.Version]
 	for _, target := range targets {
 		work.Add(target)
 	}
-	work.Do(10, func(item any) {
-		m := item.(module.Version)
+	work.Do(10, func(m module.Version) {
 
 		var required []module.Version
 		var err error
diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go
index 7626251..8912a3a 100644
--- a/src/cmd/go/internal/par/work.go
+++ b/src/cmd/go/internal/par/work.go
@@ -13,25 +13,25 @@
 
 // Work manages a set of work items to be executed in parallel, at most once each.
 // The items in the set must all be valid map keys.
-type Work struct {
-	f       func(any) // function to run for each item
-	running int       // total number of runners
+type Work[T comparable] struct {
+	f       func(T) // function to run for each item
+	running int     // total number of runners
 
 	mu      sync.Mutex
-	added   map[any]bool // items added to set
-	todo    []any        // items yet to be run
-	wait    sync.Cond    // wait when todo is empty
-	waiting int          // number of runners waiting for todo
+	added   map[T]bool // items added to set
+	todo    []T        // items yet to be run
+	wait    sync.Cond  // wait when todo is empty
+	waiting int        // number of runners waiting for todo
 }
 
-func (w *Work) init() {
+func (w *Work[T]) init() {
 	if w.added == nil {
-		w.added = make(map[any]bool)
+		w.added = make(map[T]bool)
 	}
 }
 
 // Add adds item to the work set, if it hasn't already been added.
-func (w *Work) Add(item any) {
+func (w *Work[T]) Add(item T) {
 	w.mu.Lock()
 	w.init()
 	if !w.added[item] {
@@ -51,7 +51,7 @@
 // before calling Do (or else Do returns immediately),
 // but it is allowed for f(item) to add new items to the set.
 // Do should only be used once on a given Work.
-func (w *Work) Do(n int, f func(item any)) {
+func (w *Work[T]) Do(n int, f func(item T)) {
 	if n < 1 {
 		panic("par.Work.Do: n < 1")
 	}
@@ -72,7 +72,7 @@
 // runner executes work in w until both nothing is left to do
 // and all the runners are waiting for work.
 // (Then all the runners return.)
-func (w *Work) runner() {
+func (w *Work[T]) runner() {
 	for {
 		// Wait for something to do.
 		w.mu.Lock()
diff --git a/src/cmd/go/internal/par/work_test.go b/src/cmd/go/internal/par/work_test.go
index add0e64..4283e0d 100644
--- a/src/cmd/go/internal/par/work_test.go
+++ b/src/cmd/go/internal/par/work_test.go
@@ -11,14 +11,13 @@
 )
 
 func TestWork(t *testing.T) {
-	var w Work
+	var w Work[int]
 
 	const N = 10000
 	n := int32(0)
 	w.Add(N)
-	w.Do(100, func(x any) {
+	w.Do(100, func(i int) {
 		atomic.AddInt32(&n, 1)
-		i := x.(int)
 		if i >= 2 {
 			w.Add(i - 1)
 			w.Add(i - 2)
@@ -33,14 +32,14 @@
 
 func TestWorkParallel(t *testing.T) {
 	for tries := 0; tries < 10; tries++ {
-		var w Work
+		var w Work[int]
 		const N = 100
 		for i := 0; i < N; i++ {
 			w.Add(i)
 		}
 		start := time.Now()
 		var n int32
-		w.Do(N, func(x any) {
+		w.Do(N, func(x int) {
 			time.Sleep(1 * time.Millisecond)
 			atomic.AddInt32(&n, +1)
 		})