Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/20250729105643.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:bug: Make sure that an improperly initialised cancel store doesn't cause a panic
7 changes: 7 additions & 0 deletions utils/parallelisation/cancel_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (

"github.com/sasha-s/go-deadlock"
"golang.org/x/sync/errgroup"

"github.com/ARM-software/golang-utils/utils/commonerrors"
"github.com/ARM-software/golang-utils/utils/reflection"
)

func newFunctionStore[T any](clearOnExecution, stopOnFirstError bool, executeFunc func(context.Context, T) error) *store[T] {
Expand Down Expand Up @@ -45,6 +48,9 @@ func (s *store[T]) Len() int {
func (s *store[T]) Execute(ctx context.Context) error {
defer s.mu.Unlock()
s.mu.Lock()
if reflection.IsEmpty(s.executeFunc) {
return commonerrors.New(commonerrors.ErrUndefined, "the cancel store was not initialised correctly")
}
g, gCtx := errgroup.WithContext(ctx)
if !s.stopOnFirstError {
gCtx = ctx
Expand Down Expand Up @@ -75,6 +81,7 @@ func (s *CancelFunctionStore) RegisterCancelFunction(cancel ...context.CancelFun
s.store.RegisterFunction(cancel...)
}

// Cancel will execute the cancel functions in the store. Any errors will be ignored and Execute() is recommended if you need to know if a cancellation failed
func (s *CancelFunctionStore) Cancel() {
_ = s.Execute(context.Background())
}
Expand Down
59 changes: 44 additions & 15 deletions utils/parallelisation/cancel_functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,62 @@
package parallelisation

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/ARM-software/golang-utils/utils/commonerrors"
"github.com/ARM-software/golang-utils/utils/commonerrors/errortest"
)

// Given a CancelFunctionsStore
// Functions can be registered
// and all functions will be called
func TestCancelFunctionStore(t *testing.T) {
// Set up some fake CancelFuncs to make sure they are called
called1 := false
called2 := false
cancelFunc1 := func() {
called1 = true
}
cancelFunc2 := func() {
called2 = true
}
t.Run("valid cancel store", func(t *testing.T) {
// Set up some fake CancelFuncs to make sure they are called
called1 := false
called2 := false
cancelFunc1 := func() {
called1 = true
}
cancelFunc2 := func() {
called2 = true
}

store := NewCancelFunctionsStore()

store.RegisterCancelFunction(cancelFunc1, cancelFunc2)

assert.Equal(t, 2, store.Len())

store.Cancel()

assert.True(t, called1)
assert.True(t, called2)
})

t.Run("incorrectly initialised cancel store", func(t *testing.T) {
called1 := false
called2 := false
cancelFunc1 := func() {
called1 = true
}
cancelFunc2 := func() {
called2 = true
}

store := NewCancelFunctionsStore()
store := CancelFunctionStore{}

store.RegisterCancelFunction(cancelFunc1, cancelFunc2)
store.RegisterCancelFunction(cancelFunc1, cancelFunc2)

assert.Equal(t, 2, store.Len())
assert.Equal(t, 2, store.Len())

store.Cancel()
err := store.Execute(context.Background())
errortest.AssertError(t, err, commonerrors.ErrUndefined)

assert.True(t, called1)
assert.True(t, called2)
assert.False(t, called1)
assert.False(t, called2)
})
}
Loading