Skip to content

Commit

Permalink
Merge pull request #40 from alitto/bug/context-error
Browse files Browse the repository at this point in the history
Return Context error if it is canceled before at least 1 task failed
  • Loading branch information
alitto authored Jan 31, 2023
2 parents a1d582c + ec43236 commit 2fbcfba
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v2
- name: Test
run: make coverage
- uses: codecov/codecov-action@v2
- uses: codecov/codecov-action@v3
with:
files: coverage.out
fail_ci_if_error: true
Expand Down
37 changes: 21 additions & 16 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,16 @@ func (g *TaskGroupWithContext) Submit(task func() error) {
defer g.waitGroup.Done()

// If context has already been cancelled, skip task execution
if g.ctx != nil {
select {
case <-g.ctx.Done():
return
default:
}
select {
case <-g.ctx.Done():
return
default:
}

// don't actually ignore errors
err := task()
if err != nil {
g.errSync.once.Do(func() {
g.errSync.guard.Lock()
g.err = err
g.errSync.guard.Unlock()

if g.cancel != nil {
g.cancel()
}
})
g.setError(err)
}
})
}
Expand All @@ -91,11 +81,26 @@ func (g *TaskGroupWithContext) Wait() error {
// If context was provided, cancel it to signal all running tasks to stop
g.cancel()
case <-g.ctx.Done():
g.setError(g.ctx.Err())
}

return g.getError()
}

func (g *TaskGroupWithContext) getError() error {
g.errSync.guard.RLock()
err := g.err
g.errSync.guard.RUnlock()

return err
}

func (g *TaskGroupWithContext) setError(err error) {
g.errSync.once.Do(func() {
g.errSync.guard.Lock()
g.err = err
g.errSync.guard.Unlock()

// Cancel execution of any pending task in this group
g.cancel()
})
}
31 changes: 31 additions & 0 deletions group_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,34 @@ func TestGroupContextWithNilContext(t *testing.T) {

assertEqual(t, "a non-nil context needs to be specified when using GroupContext", thrownPanic)
}

func TestGroupContextWithCanceledContext(t *testing.T) {

pool := pond.New(3, 100)
assertEqual(t, 0, pool.RunningWorkers())

// Submit a group of tasks
var doneCount, startedCount int32
userCtx, cancel := context.WithCancel(context.Background())
group, ctx := pool.GroupContext(userCtx)
for i := 0; i < 10; i++ {
group.Submit(func() error {
atomic.AddInt32(&startedCount, 1)

select {
case <-time.After(10 * time.Millisecond):
atomic.AddInt32(&doneCount, 1)
case <-ctx.Done():
}

return nil
})
}

// Cancel context right after submitting tasks
cancel()

err := group.Wait()
assertEqual(t, context.Canceled, err)
assertEqual(t, int32(0), atomic.LoadInt32(&doneCount))
}

0 comments on commit 2fbcfba

Please sign in to comment.