fn: sync.WaitGroup replacement common.WaitGroup (#937)

* fn: sync.WaitGroup replacement common.WaitGroup

agent/lb_agent/pure_runner has been incorrectly using
sync.WaitGroup semantics. Switching these components to
use the new common.WaitGroup() that provides a few handy
functionality for common graceful shutdown cases.

From https://golang.org/pkg/sync/#WaitGroup,
    "Note that calls with a positive delta that occur when the counter
     is zero must happen before a Wait. Calls with a negative delta,
     or calls with a positive delta that start when the counter is
     greater than zero, may happen at any time. Typically this means
     the calls to Add should execute before the statement creating
     the goroutine or other event to be waited for. If a WaitGroup
     is reused to wait for several independent sets of events,
     new Add calls must happen after all previous Wait calls have
     returned."

HandleCallEnd introduces some complexity to the shutdowns, but this
is currently handled by AddSession(2) initially and letting the
HandleCallEnd() when to decrement by -1 in addition to decrement -1 in
Submit().

lb_agent shutdown sequence and particularly timeouts with runner pool
needs another look/revision, but this is outside of the scope of this
commit.

* fn: lb-agent wg share

* fn: no need to +2 in Submit with defer.

Removed defer since handleCallEnd already has
this responsibility.
This commit is contained in:
Tolga Ceylan
2018-04-12 11:33:01 -07:00
committed by GitHub
parent f350b2ca48
commit e53d23afc9
7 changed files with 298 additions and 99 deletions

View File

@@ -0,0 +1,124 @@
package common
import (
"testing"
)
func isClosed(ch chan struct{}) bool {
select {
case <-ch:
return true
default:
}
return false
}
func TestWaitGroupEmpty(t *testing.T) {
wg := NewWaitGroup()
if !wg.AddSession(0) {
t.Fatalf("Add 0 should not fail")
}
if isClosed(wg.Closer()) {
t.Fatalf("Should not be closed yet")
}
done := wg.CloseGroupNB()
// gate-on close
wg.CloseGroup()
if !isClosed(wg.Closer()) {
t.Fatalf("Should be closing state")
}
if isClosed(done) {
t.Fatalf("NB Chan I should be closed")
}
done = wg.CloseGroupNB()
if isClosed(done) {
t.Fatalf("NB Chan II should be closed")
}
}
func TestWaitGroupSingle(t *testing.T) {
wg := NewWaitGroup()
if isClosed(wg.Closer()) {
t.Fatalf("Should not be closing state yet")
}
if !wg.AddSession(1) {
t.Fatalf("Add 1 should not fail")
}
if isClosed(wg.Closer()) {
t.Fatalf("Should not be closing state yet")
}
if !wg.AddSession(-1) {
t.Fatalf("Add -1 should not fail")
}
// sum should be zero now.
if !wg.AddSession(2) {
t.Fatalf("Add 2 should not fail")
}
// sum is 2 now
// initiate shutdown
done := wg.CloseGroupNB()
if isClosed(done) {
t.Fatalf("NB Chan should not be closed yet, since sum is 2")
}
if !wg.AddSession(-1) {
t.Fatalf("Add -1 should not fail")
}
if wg.AddSession(1) {
t.Fatalf("Add 1 should fail (we are shutting down)")
}
if !isClosed(wg.Closer()) {
t.Fatalf("Should be closing state")
}
// sum is 1 now
if isClosed(done) {
t.Fatalf("NB Chan should not be closed yet, since sum is 1")
}
if wg.AddSession(0) {
t.Fatalf("Add 0 should fail (considered positive number and we are closing)")
}
if wg.AddSession(100) {
t.Fatalf("Add 100 should fail (we are shutting down)")
}
if !isClosed(wg.Closer()) {
t.Fatalf("Should be closing state")
}
if !wg.AddSession(-1) {
t.Fatalf("Add -1 should not fail")
}
// sum is 0 now
<-done
if !isClosed(done) {
t.Fatalf("NB Chan should be closed, since sum is 0")
}
if !isClosed(wg.Closer()) {
t.Fatalf("Should be closing state")
}
}