This repository has been archived by the owner on Nov 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xcontext.go
209 lines (189 loc) · 6.55 KB
/
xcontext.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Package xcontext provides extended utilities for context.
package xcontext
import (
"context"
"sync"
"time"
)
// DelayContext creates a new cancel context not inherited from ctx.
// It would be cancelled if ctx is done, after the delay started with the call of DelayContext is done.
// The new context doesn't inherit ctx.
// Calling the cancel function is not necessary.
func DelayContext(ctx context.Context, delay time.Duration) (context.Context, context.CancelFunc) {
newCtx, newCtxCancel := context.WithCancel(context.Background())
delayTimer := time.NewTimer(delay)
go func() {
parentCtx := ctx
parentCtxOk := context.Background()
delayCh := delayTimer.C
delayOkCh := make(<-chan time.Time)
for done := false; !done; {
select {
case <-parentCtx.Done():
parentCtx = parentCtxOk
if delayCh == delayOkCh {
done = true
}
case <-delayCh:
delayCh = delayOkCh
if parentCtx == parentCtxOk {
done = true
}
case <-newCtx.Done():
done = true
}
}
newCtxCancel()
delayTimer.Stop()
}()
return newCtx, newCtxCancel
}
// DelayContext2 is similar with DelayContext except not returns cancel function.
func DelayContext2(ctx context.Context, delay time.Duration) context.Context {
newCtx, _ := DelayContext(ctx, delay)
return newCtx
}
// DelayAfterContext creates a new cancel context not inherited from ctx.
// It would be cancelled after the delay started when ctx is done.
// The new context doesn't inherit ctx.
// Calling the cancel function is not necessary.
func DelayAfterContext(ctx context.Context, delay time.Duration) (context.Context, context.CancelFunc) {
newCtx, newCtxCancel := context.WithCancel(context.Background())
delayTimer := time.NewTimer(0)
if !delayTimer.Stop() {
<-delayTimer.C
}
go func() {
parentCtx := ctx
parentCtxOk := context.Background()
for done := false; !done; {
select {
case <-parentCtx.Done():
parentCtx = parentCtxOk
delayTimer.Reset(delay)
case <-delayTimer.C:
if parentCtx == parentCtxOk {
done = true
}
case <-newCtx.Done():
done = true
}
}
newCtxCancel()
delayTimer.Stop()
}()
return newCtx, newCtxCancel
}
// DelayAfterContext2 is similar with DelayAfterContext except not returns cancel function.
func DelayAfterContext2(ctx context.Context, delay time.Duration) context.Context {
newCtx, _ := DelayAfterContext(ctx, delay)
return newCtx
}
// MultiContext creates a new cancel context inherited from parent.
// It would be cancelled when at least one of the sub contexts is done.
// Calling the cancel function is not necessary.
func MultiContext(parent context.Context, subs ...context.Context) (context.Context, context.CancelFunc) {
newCtx, newCtxCancel := context.WithCancel(parent)
for _, sub := range subs {
go func(sub context.Context) {
select {
case <-sub.Done():
case <-newCtx.Done():
}
newCtxCancel()
}(sub)
}
return newCtx, newCtxCancel
}
// MultiContext2 is similar with MultiContext except not returns cancel function.
func MultiContext2(parent context.Context, subs ...context.Context) context.Context {
newCtx, _ := MultiContext(parent, subs...)
return newCtx
}
// WaitContext creates a new cancel context inherited from parent.
// It would be cancelled when all the sub contexts are done.
// Calling the cancel function is not necessary.
func WaitContext(parent context.Context, subs ...context.Context) (context.Context, context.CancelFunc) {
newCtx, newCtxCancel := context.WithCancel(parent)
var wg sync.WaitGroup
for _, sub := range subs {
wg.Add(1)
go func(sub context.Context) {
select {
case <-sub.Done():
case <-newCtx.Done():
}
wg.Done()
}(sub)
}
go func() {
wg.Wait()
newCtxCancel()
}()
return newCtx, newCtxCancel
}
// WaitContext2 is similar with WaitContext except not returns cancel function.
func WaitContext2(parent context.Context, subs ...context.Context) context.Context {
newCtx, _ := WaitContext(parent, subs...)
return newCtx
}
// Or creates a new cancel context to cancel when at least one of the contexts is done.
// The new context doesn't inherit any context in ctxs.
func Or(ctxs ...context.Context) (context.Context, context.CancelFunc) {
return MultiContext(context.Background(), ctxs...)
}
// Or2 is similar with Or except not returns cancel function.
func Or2(ctxs ...context.Context) context.Context {
return MultiContext2(context.Background(), ctxs...)
}
// And creates a new cancel context to cancel when all the contexts are done.
// The new context doesn't inherit any context in ctxs.
func And(ctxs ...context.Context) (context.Context, context.CancelFunc) {
return WaitContext(context.Background(), ctxs...)
}
// And2 is similar with And except not returns cancel function.
func And2(ctxs ...context.Context) context.Context {
return WaitContext2(context.Background(), ctxs...)
}
// AutoCancel cancels the underlying context with cancel function when it was done through parent, deadline, timeout or in any way.
// It returns ctx and cancel.
func AutoCancel(ctx context.Context, cancel context.CancelFunc) (context.Context, context.CancelFunc) {
go func() {
<-ctx.Done()
cancel()
}()
return ctx, cancel
}
// WithCancel is similar with context.WithCancel.
// But it cancels the context when it was done through parent.
func WithCancel(parent context.Context) (context.Context, context.CancelFunc) {
return AutoCancel(context.WithCancel(parent))
}
// WithCancel2 is similar with WithCancel.
// It returns only a new context inherited from parent.
func WithCancel2(parent context.Context) context.Context {
ctx, _ := WithCancel(parent)
return ctx
}
// WithDeadline is similar with context.WithDeadline.
// But it cancels the context when it was done through parent or deadline.
func WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) {
return AutoCancel(context.WithDeadline(parent, d))
}
// WithDeadline2 is similar with WithDeadline.
// It returns only a new context inherited from parent.
func WithDeadline2(parent context.Context, d time.Time) context.Context {
ctx, _ := WithDeadline(parent, d)
return ctx
}
// WithTimeout is similar with context.WithTimeout.
// But it cancels the context when it was done through parent or timeout.
func WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
return AutoCancel(context.WithTimeout(parent, timeout))
}
// WithTimeout2 is similar with WithTimeout.
// It returns only a new context inherited from parent.
func WithTimeout2(parent context.Context, timeout time.Duration) context.Context {
ctx, _ := WithTimeout(parent, timeout)
return ctx
}