-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Fix deadlock raised in #11 #13
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,8 +40,10 @@ type Group struct { | |
|
||
wg sync.WaitGroup | ||
|
||
errOnce sync.Once | ||
err error | ||
err error | ||
|
||
// errMu protects err. | ||
errMu sync.RWMutex | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// numG is the maximum number of goroutines that can be started. | ||
numG int | ||
|
@@ -154,13 +156,26 @@ func (g *Group) Go(f func() error) { | |
return | ||
} | ||
|
||
g.qCh <- f | ||
for { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this for loop we simply do the following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only concern with this is it could become a busy loop. I wonder if |
||
g.errMu.RLock() | ||
if g.err != nil { | ||
g.errMu.RUnlock() | ||
g.qMu.Unlock() | ||
|
||
// Check if we can or should start a new goroutine? | ||
g.maybeStartG() | ||
return | ||
} | ||
|
||
g.qMu.Unlock() | ||
select { | ||
case g.qCh <- f: | ||
g.errMu.RUnlock() | ||
g.maybeStartG() | ||
g.qMu.Unlock() | ||
|
||
return | ||
default: | ||
g.errMu.RUnlock() | ||
} | ||
} | ||
} | ||
|
||
// maybeStartG might start a new worker goroutine, if | ||
|
@@ -204,16 +219,30 @@ func (g *Group) startG() { | |
return | ||
} | ||
|
||
if err := f(); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks nicer but as explained above we are already needing the So let me know if you think its better to stay using sync.Once in addition to the RWMutex just for readability. |
||
g.errOnce.Do(func() { | ||
g.err = err | ||
if g.cancel != nil { | ||
g.cancel() | ||
} | ||
}) | ||
err := f() | ||
if err == nil { | ||
// happy path | ||
continue | ||
} | ||
|
||
// an error exists | ||
// checking if it's the first group error | ||
g.errMu.Lock() | ||
if g.err != nil { | ||
// this is not the first group error | ||
// no need to set it | ||
g.errMu.Unlock() | ||
return | ||
} | ||
|
||
g.err = err | ||
g.errMu.Unlock() | ||
|
||
if g.cancel != nil { | ||
g.cancel() | ||
} | ||
|
||
return | ||
} | ||
}() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since now we will be reading
err
continuously then we needed anRWMutex
already.We could keep the
sync.Once
but the same can be achieved withRWMutex
that is already needed, so removed thesync.Once
. Let me know if you think it's cleaner to keep bothsync.Once
andsync.RWMutex
.