diff --git a/pkg/util/goroutinemap/goroutinemap.go b/pkg/util/goroutinemap/goroutinemap.go index cbf8cd3462a..cd6682f2fc2 100644 --- a/pkg/util/goroutinemap/goroutinemap.go +++ b/pkg/util/goroutinemap/goroutinemap.go @@ -62,7 +62,7 @@ func (grm *goRoutineMap) Run(operationName string, operation func() error) error defer grm.Unlock() if grm.operations[operationName] { // Operation with name exists - return fmt.Errorf("Failed to create operation with name %q. An operation with that name already exists.", operationName) + return newAlreadyExistsError(operationName) } grm.operations[operationName] = true @@ -86,3 +86,30 @@ func (grm *goRoutineMap) operationComplete(operationName string) { func (grm *goRoutineMap) Wait() { grm.wg.Wait() } + +// alreadyExistsError is specific error returned when NewGoRoutine() +// detects that operation with given name is already running. +type alreadyExistsError struct { + operationName string +} + +var _ error = alreadyExistsError{} + +func (err alreadyExistsError) Error() string { + return fmt.Sprintf("Failed to create operation with name %q. An operation with that name already exists", err.operationName) +} + +func newAlreadyExistsError(operationName string) error { + return alreadyExistsError{operationName} +} + +// IsAlreadyExists returns true if an error returned from NewGoRoutine indicates +// that operation with the same name already exists. +func IsAlreadyExists(err error) bool { + switch err.(type) { + case alreadyExistsError: + return true + default: + return false + } +} diff --git a/pkg/util/goroutinemap/goroutinemap_test.go b/pkg/util/goroutinemap/goroutinemap_test.go index 04d205c45de..466c0f1ba58 100644 --- a/pkg/util/goroutinemap/goroutinemap_test.go +++ b/pkg/util/goroutinemap/goroutinemap_test.go @@ -125,6 +125,9 @@ func Test_NewGoRoutineMap_Negative_SecondOpBeforeFirstCompletes(t *testing.T) { if err2 == nil { t.Fatalf("NewGoRoutine did not fail. Expected: Actual: ", operationName) } + if !IsAlreadyExists(err2) { + t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2) + } } func Test_NewGoRoutineMap_Positive_ThirdOpAfterFirstCompletes(t *testing.T) { @@ -147,6 +150,9 @@ func Test_NewGoRoutineMap_Positive_ThirdOpAfterFirstCompletes(t *testing.T) { if err2 == nil { t.Fatalf("NewGoRoutine did not fail. Expected: Actual: ", operationName) } + if !IsAlreadyExists(err2) { + t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2) + } // Act operation1DoneCh <- true // Force operation1 to complete