forked from projectdiscovery/utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNContext.go
91 lines (84 loc) · 2.54 KB
/
NContext.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package contextutil
import "context"
// A problematic situation when implementing context in a function
// is when that function has more than one return values
// if function has only one return value we can safely wrap it something like this
/*
func DoSomething() error {}
ch := make(chan error)
go func() {
ch <- DoSomething()
}()
select {
case err := <-ch:
// handle error
case <-ctx.Done():
// handle context cancelation
}
*/
// but what if we have more than one value to return?
// we can use generics and a struct and that is what we are doing here
// here we use struct and generics to store return values of a function
// instead of storing it in a []interface{}
type twoValueCtx[T1 any, T2 any] struct {
var1 T1
var2 T2
}
type threeValueCtx[T1 any, T2 any, T3 any] struct {
var1 T1
var2 T2
var3 T3
}
// ExecFunc implements context for a function which has no return values
// and executes that function. if context is cancelled before function returns
// it will return context error otherwise it will return nil
func ExecFunc(ctx context.Context, fn func()) error {
ch := make(chan struct{})
go func() {
fn()
ch <- struct{}{}
}()
select {
case <-ch:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
// ExecFuncWithTwoReturns wraps a function which has two return values given that last one is error
// and executes that function in a goroutine there by implementing context
// if context is cancelled before function returns it will return context error
// otherwise it will return function's return values
func ExecFuncWithTwoReturns[T1 any](ctx context.Context, fn func() (T1, error)) (T1, error) {
ch := make(chan twoValueCtx[T1, error])
go func() {
x, y := fn()
ch <- twoValueCtx[T1, error]{var1: x, var2: y}
}()
select {
case <-ctx.Done():
var tmp T1
return tmp, ctx.Err()
case v := <-ch:
return v.var1, v.var2
}
}
// ExecFuncWithThreeReturns wraps a function which has three return values given that last one is error
// and executes that function in a goroutine there by implementing context
// if context is cancelled before function returns it will return context error
// otherwise it will return function's return values
func ExecFuncWithThreeReturns[T1 any, T2 any](ctx context.Context, fn func() (T1, T2, error)) (T1, T2, error) {
ch := make(chan threeValueCtx[T1, T2, error])
go func() {
x, y, z := fn()
ch <- threeValueCtx[T1, T2, error]{var1: x, var2: y, var3: z}
}()
select {
case <-ctx.Done():
var tmp1 T1
var tmp2 T2
return tmp1, tmp2, ctx.Err()
case v := <-ch:
return v.var1, v.var2, v.var3
}
}