diff --git a/merrors/doc.go b/merrors/doc.go index 0ea02d3..a670493 100644 --- a/merrors/doc.go +++ b/merrors/doc.go @@ -19,12 +19,11 @@ // // Example 3: // -// func CloseAll(closers []io.Closer) error { -// errs := merrors.New() -// for _ , c := range closers { -// errs.Add(c.Close()) -// } -// return errs.Err() -// } -// +// func CloseAll(closers []io.Closer) error { +// errs := merrors.New() +// for _ , c := range closers { +// errs.Add(c.Close()) +// } +// return errs.Err() +// } package merrors diff --git a/merrors/merrors.go b/merrors/merrors.go index bb0c9dc..3255900 100644 --- a/merrors/merrors.go +++ b/merrors/merrors.go @@ -8,6 +8,7 @@ import ( stderrors "errors" "fmt" "io" + "sync" ) // NilOrMultiError type allows combining multiple errors into one. @@ -38,11 +39,42 @@ func (e *NilOrMultiError) Add(errs ...error) { } // Err returns the error list as an Error (also implements error) or nil if it is empty. -func (e NilOrMultiError) Err() Error { - if len(e.errs) == 0 { +func (e *NilOrMultiError) Err() Error { + if e == nil || len(e.errs) == 0 { return nil } - return multiError(e) + + return multiError(*e) +} + +// NilOrMultiSyncError is a thread-safe implementation of NilOrMultiError. +// It allows combining multiple errors into one. +type NilOrMultiSyncError struct { + mtx sync.Mutex + multiErr NilOrMultiError +} + +// NewSync returns NilOrMultiSyncError with provided errors added if not nil. +func NewSync(errs ...error) *NilOrMultiSyncError { + sm := &NilOrMultiSyncError{} + sm.Add(errs...) + return sm +} + +// Add adds single or many errors to the error list. It has same behavior as NilOrMultiError. +func (e *NilOrMultiSyncError) Add(errs ...error) { + e.mtx.Lock() + defer e.mtx.Unlock() + + e.multiErr.Add(errs...) +} + +// Err returns the error list as an Error (also implements error) or nil if it is empty. +func (e *NilOrMultiSyncError) Err() Error { + e.mtx.Lock() + defer e.mtx.Unlock() + + return e.multiErr.Err() } // Error is extended error interface that allows to use returned read-only multi error in more advanced ways. diff --git a/merrors/merrors_test.go b/merrors/merrors_test.go index 7825d69..07703fb 100644 --- a/merrors/merrors_test.go +++ b/merrors/merrors_test.go @@ -63,6 +63,26 @@ func TestMultiError(t *testing.T) { }()) } +func TestMultiSyncError(t *testing.T) { + err := errors.New("test1") + + // Run tests with race detector to detect data races in NilOrMultiSyncError. + e := merrors.NewSync(err) + for i := 0; i < 10; i++ { + go func() { + e.Add(err) + }() + } + + testutil.NotOk(t, func() error { + return e.Err() + }()) + + testutil.Ok(t, func() error { + return merrors.NewSync(nil, nil, nil).Err() + }()) +} + func TestMultiError_Error(t *testing.T) { err := errors.New("test1")