Skip to content

Commit d1e652e

Browse files
committed
add few utils functions
1 parent 58ee40e commit d1e652e

File tree

2 files changed

+152
-1
lines changed

2 files changed

+152
-1
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/negrel/conc
22

3-
go 1.20
3+
go 1.23

utils.go

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package conc
2+
3+
import (
4+
"context"
5+
"iter"
6+
"sync/atomic"
7+
"time"
8+
)
9+
10+
// Sleep is an alternative to time.Sleep that returns once d time is elapsed or
11+
// context is done.
12+
func Sleep(ctx context.Context, d time.Duration) {
13+
select {
14+
case <-ctx.Done():
15+
case <-time.After(d):
16+
}
17+
}
18+
19+
type Job[T any] func(context.Context) (T, error)
20+
21+
// All executes all jobs in separate goroutines and stores each result in
22+
// the returned slice.
23+
func All[T any](jobs []Job[T], opts ...BlockOption) []T {
24+
results := make([]T, len(jobs), len(jobs))
25+
26+
Block(func(n Nursery) error {
27+
for i, job := range jobs {
28+
r := &results[i]
29+
n.Go(func() (err error) {
30+
*r, err = job(n)
31+
return err
32+
})
33+
}
34+
35+
return nil
36+
}, opts...)
37+
38+
return results
39+
}
40+
41+
// Any executes all jobs in separate goroutines and returns first result.
42+
// Remaining goroutines are cancelled.
43+
func Any[T any](jobs []Job[T], opts ...BlockOption) T {
44+
var first atomic.Bool
45+
var result T
46+
47+
Block(func(n Nursery) error {
48+
for _, job := range jobs {
49+
n.Go(func() (err error) {
50+
r, err := job(n)
51+
if first.CompareAndSwap(false, true) {
52+
result = r
53+
n.(*nursery).cancel()
54+
}
55+
return err
56+
})
57+
}
58+
59+
return nil
60+
}, opts...)
61+
62+
return result
63+
}
64+
65+
// Range iterates over a sequence and pass each value to a separate goroutine.
66+
func Range[T any](seq iter.Seq[T], block func(context.Context, T) error, opts ...BlockOption) {
67+
Block(func(n Nursery) error {
68+
for v := range seq {
69+
value := v
70+
n.Go(func() error {
71+
return block(n, value)
72+
})
73+
}
74+
75+
return nil
76+
}, opts...)
77+
}
78+
79+
// Range2 is the same as Range except it uses a iter.Seq2 instead of iter.Seq.
80+
func Range2[K, V any](seq iter.Seq2[K, V], block func(context.Context, K, V) error, opts ...BlockOption) {
81+
Block(func(n Nursery) error {
82+
for k, v := range seq {
83+
key := k
84+
value := v
85+
n.Go(func() error {
86+
return block(n, key, value)
87+
})
88+
}
89+
90+
return nil
91+
}, opts...)
92+
}
93+
94+
// Map applies f to each element of input and returns a new slice containing
95+
// mapped results.
96+
func Map[T any](input []T, f func(context.Context, T) (T, error), opts ...BlockOption) []T {
97+
results := make([]T, len(input), len(input))
98+
doMap(input, results, f, opts...)
99+
return results
100+
}
101+
102+
// MapInPlace applies f to each element of input and returns modified input slice.
103+
func MapInPlace[T any](input []T, f func(context.Context, T) (T, error), opts ...BlockOption) []T {
104+
doMap(input, input, f, opts...)
105+
return input
106+
}
107+
108+
func doMap[T any](input []T, results []T, f func(context.Context, T) (T, error), opts ...BlockOption) {
109+
Block(func(n Nursery) error {
110+
for i, v := range input {
111+
value := v
112+
r := &results[i]
113+
n.Go(func() (err error) {
114+
*r, err = f(n, value)
115+
return err
116+
})
117+
}
118+
119+
return nil
120+
}, opts...)
121+
}
122+
123+
// Map2 applies f to each key, value pair of input and returns a new slice containing
124+
// mapped results.
125+
func Map2[K comparable, V any](input map[K]V, f func(context.Context, K, V) (K, V, error), opts ...BlockOption) map[K]V {
126+
results := make(map[K]V)
127+
doMap2(input, results, f, opts...)
128+
return results
129+
}
130+
131+
// MapInPlace2 applies f to each key, value pair of input and returns modified map.
132+
func Map2InPlace[K comparable, V any](input map[K]V, f func(context.Context, K, V) (K, V, error), opts ...BlockOption) map[K]V {
133+
doMap2(input, input, f, opts...)
134+
return input
135+
}
136+
137+
func doMap2[K comparable, V any](input map[K]V, results map[K]V, f func(context.Context, K, V) (K, V, error), opts ...BlockOption) {
138+
Block(func(n Nursery) error {
139+
for k, v := range input {
140+
key := k
141+
value := v
142+
n.Go(func() error {
143+
newK, newV, err := f(n, key, value)
144+
results[newK] = newV
145+
return err
146+
})
147+
}
148+
149+
return nil
150+
}, opts...)
151+
}

0 commit comments

Comments
 (0)