@@ -3,6 +3,7 @@ package async
3
3
import (
4
4
"context"
5
5
"sync"
6
+ "sync/atomic"
6
7
)
7
8
8
9
// builtinPool is the Parallelers pool for built-in functions.
@@ -76,7 +77,7 @@ func (p *Paralleler) Run() ([][]any, error) {
76
77
77
78
ch := make (chan executeResult , len (tasks ))
78
79
79
- go p .runTasks (ctx , ch , tasks )
80
+ go p .runTasks (ctx , ch , tasks , true )
80
81
81
82
finished := 0
82
83
for finished < len (tasks ) {
@@ -98,6 +99,41 @@ func (p *Paralleler) Run() ([][]any, error) {
98
99
return out , nil
99
100
}
100
101
102
+ // RunCompleted runs the tasks in the paralleler's pending list until all functions are finished,
103
+ // it'll clear the pending list and return the results of the tasks.
104
+ func (p * Paralleler ) RunCompleted () ([][]any , error ) {
105
+ tasks := p .getTasks ()
106
+ out := make ([][]any , len (tasks ))
107
+ if len (tasks ) == 0 {
108
+ return out , nil
109
+ }
110
+
111
+ errs := make ([]error , len (tasks ))
112
+ errNum := atomic.Int32 {}
113
+ parent := getContext (p .ctx )
114
+ ctx , canFunc := context .WithCancel (parent )
115
+ defer canFunc ()
116
+
117
+ ch := make (chan executeResult , len (tasks ))
118
+
119
+ go p .runTasks (ctx , ch , tasks , false )
120
+
121
+ for finished := 0 ; finished < len (tasks ); finished ++ {
122
+ ret := <- ch
123
+ out [ret .Index ] = ret .Out
124
+ if ret .Error != nil {
125
+ errs [ret .Index ] = ret .Error
126
+ errNum .Add (1 )
127
+ }
128
+ }
129
+
130
+ if errNum .Load () == 0 {
131
+ return out , nil
132
+ }
133
+
134
+ return out , convertErrorListToExecutionErrors (errs , int (errNum .Load ()))
135
+ }
136
+
101
137
// getConcurrencyChan creates and returns a concurrency controlling channel by the specific number
102
138
// of the concurrency limitation.
103
139
func (p * Paralleler ) getConcurrencyChan () chan empty {
@@ -124,15 +160,20 @@ func (p *Paralleler) getTasks() []AsyncFn {
124
160
}
125
161
126
162
// runTasks runs the tasks with the concurrency limitation.
127
- func (p * Paralleler ) runTasks (ctx context.Context , resCh chan executeResult , tasks []AsyncFn ) {
163
+ func (p * Paralleler ) runTasks (
164
+ ctx context.Context ,
165
+ resCh chan executeResult ,
166
+ tasks []AsyncFn ,
167
+ exitWhenDone bool ,
168
+ ) {
128
169
conch := p .getConcurrencyChan ()
129
170
130
171
for i := 0 ; i < len (tasks ); i ++ {
131
172
if conch != nil {
132
173
conch <- empty {}
133
174
}
134
175
135
- go p .runTask (ctx , i , tasks [i ], conch , resCh )
176
+ go p .runTask (ctx , i , tasks [i ], conch , resCh , exitWhenDone )
136
177
}
137
178
}
138
179
@@ -143,6 +184,7 @@ func (p *Paralleler) runTask(
143
184
fn AsyncFn ,
144
185
conch chan empty ,
145
186
ch chan executeResult ,
187
+ exitWhenDone bool ,
146
188
) {
147
189
childCtx , childCanFunc := context .WithCancel (ctx )
148
190
defer childCanFunc ()
@@ -153,14 +195,22 @@ func (p *Paralleler) runTask(
153
195
<- conch
154
196
}
155
197
156
- select {
157
- case <- ctx .Done ():
158
- return
159
- default :
198
+ if ! exitWhenDone {
160
199
ch <- executeResult {
161
200
Index : n ,
162
201
Error : err ,
163
202
Out : ret ,
164
203
}
204
+ } else {
205
+ select {
206
+ case <- ctx .Done ():
207
+ return
208
+ default :
209
+ ch <- executeResult {
210
+ Index : n ,
211
+ Error : err ,
212
+ Out : ret ,
213
+ }
214
+ }
165
215
}
166
216
}
0 commit comments