58
// for the whole batch that the item ended up in, and a context error. Even if Add returns a
59
// context error, the item may still be processed in the future!
60
>
func (b *Batcher[T, R]) Add(ctx context.Context, t T) (R, error) {
batcher.go
61
>
resp := make(chan R, 1)
62
>
pair := batchPair[T, R]{resp: resp, item: t}
63
>
64
>
for {
65
>
runningC := b.running.Load()
66
>
for runningC == nil {
67
>
// goroutine is not running, try to start it
68
>
newRunningC := make(chan struct{})
69
>
if b.running.CompareAndSwap(nil, &newRunningC) {
70
>
// we were the first one to notice the nil, start it now
71
>
go b.loop(&newRunningC)
72
>
}
73
// if CompareAndSwap failed, someone else was calling Add at the same time and
74
// started the goroutine already. reload to get the new running channel.
76
}
77
79
case <-(*runningC):
80
// we loaded a non-nil running channel, but it closed while we're waiting to
81
// submit. the goroutine must have just exited. try again.
82
continue
84
>
select {
85
>
case r := <-resp:
86
>
return r, nil
87
case <-ctx.Done():
88
var zeroR R