@@ -3,6 +3,7 @@ package query
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "sync"
6
7
"time"
7
8
)
8
9
@@ -149,6 +150,7 @@ type Results interface {
149
150
NextSync () (Result , bool ) // blocks and waits to return the next result, second parameter returns false when results are exhausted
150
151
Rest () ([]Entry , error ) // waits till processing finishes, returns all entries at once.
151
152
Close () // client may call Close to signal early exit
153
+ Done () <- chan struct {} // signals that Results is closed
152
154
}
153
155
154
156
// results implements Results
@@ -189,6 +191,10 @@ func (r *results) Query() Query {
189
191
return r .query
190
192
}
191
193
194
+ func (r * results ) Done () <- chan struct {} {
195
+ return r .ctx .Done ()
196
+ }
197
+
192
198
// ResultBuilder is what implementors use to construct results
193
199
// Implementors of datastores and their clients must respect the
194
200
// Process of the Request:
@@ -205,6 +211,7 @@ type ResultBuilder struct {
205
211
206
212
ctx context.Context
207
213
cancel context.CancelFunc
214
+ wg sync.WaitGroup
208
215
}
209
216
210
217
// Results returns a Results to to this builder.
@@ -232,6 +239,7 @@ func NewResultBuilder(q Query) *ResultBuilder {
232
239
}
233
240
b .ctx , b .cancel = context .WithCancel (context .Background ())
234
241
context .AfterFunc (b .ctx , func () {
242
+ b .wg .Wait ()
235
243
close (b .Output )
236
244
})
237
245
return b
@@ -240,8 +248,11 @@ func NewResultBuilder(q Query) *ResultBuilder {
240
248
// ResultsWithChan returns a Results object from a channel
241
249
// of Result entries.
242
250
//
243
- // DEPRECATED: This iterator is impossible to cancel correctly. Canceling it
244
- // will leave anything trying to write to the result channel hanging.
251
+ // DEPRECATED: This iterator takes sepcial care to cancel correctly. Canceling
252
+ // it will leave anything trying to write to the result channel hanging, unless
253
+ // that write can select the result channel and Results.Done(). This requires
254
+ // creating the result channel, calline ResultsWithChan, and then writing to
255
+ // the results channel.
245
256
func ResultsWithChan (q Query , res <- chan Result ) Results {
246
257
return ResultsWithContext (q , func (ctx context.Context , out chan <- Result ) {
247
258
for {
@@ -263,14 +274,15 @@ func ResultsWithChan(q Query, res <-chan Result) Results {
263
274
})
264
275
}
265
276
266
- // ResultsWithCtxs returns a Results object with the results generated by the
267
- // passed proc function called in a separate goroutine.
277
+ // ResultsWithContext returns a Results object with the results generated by
278
+ // the passed proc function called in a separate goroutine.
268
279
func ResultsWithContext (q Query , proc func (context.Context , chan <- Result )) Results {
269
280
b := NewResultBuilder (q )
270
-
281
+ b . wg . Add ( 1 )
271
282
go func () {
272
- defer b .cancel ()
273
283
proc (b .ctx , b .Output )
284
+ b .cancel ()
285
+ b .wg .Done ()
274
286
}()
275
287
276
288
return b .Results ()
@@ -381,6 +393,11 @@ func (r *resultsIter) Query() Query {
381
393
return r .query
382
394
}
383
395
396
+ func (r * resultsIter ) Done () <- chan struct {} {
397
+ r .useLegacyResults ()
398
+ return r .legacyResults .Done ()
399
+ }
400
+
384
401
func (r * resultsIter ) useLegacyResults () {
385
402
if r .legacyResults != nil {
386
403
return
0 commit comments