2018-12-05 17:17:33 +00:00
|
|
|
package allocwatcher
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
|
|
|
)
|
|
|
|
|
|
|
|
type groupPrevAllocWatcher struct {
|
|
|
|
prevAllocs []PrevAllocWatcher
|
|
|
|
wg sync.WaitGroup
|
|
|
|
|
|
|
|
// waiting and migrating are true when alloc runner is waiting on the
|
|
|
|
// prevAllocWatcher. Writers must acquire the waitingLock and readers
|
|
|
|
// should use the helper methods IsWaiting and IsMigrating.
|
|
|
|
waiting bool
|
|
|
|
waitingLock sync.RWMutex
|
|
|
|
}
|
|
|
|
|
2018-12-06 11:15:59 +00:00
|
|
|
func NewGroupAllocWatcher(watchers ...PrevAllocWatcher) PrevAllocWatcher {
|
2018-12-05 17:17:33 +00:00
|
|
|
return &groupPrevAllocWatcher{
|
|
|
|
prevAllocs: watchers,
|
2018-12-06 11:15:59 +00:00
|
|
|
}
|
2018-12-05 17:17:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait on the previous allocs to become terminal, exit, or, return due to
|
|
|
|
// context termination. Usage of the groupPrevAllocWatcher requires that all
|
|
|
|
// sub-watchers correctly handle context cancellation.
|
|
|
|
// We may need to adjust this to use channels rather than a wait group, if we
|
|
|
|
// wish to more strictly enforce timeouts.
|
|
|
|
func (g *groupPrevAllocWatcher) Wait(ctx context.Context) error {
|
|
|
|
g.waitingLock.Lock()
|
|
|
|
g.waiting = true
|
|
|
|
g.waitingLock.Unlock()
|
|
|
|
defer func() {
|
|
|
|
g.waitingLock.Lock()
|
|
|
|
g.waiting = false
|
|
|
|
g.waitingLock.Unlock()
|
|
|
|
}()
|
|
|
|
|
|
|
|
var merr multierror.Error
|
|
|
|
var errmu sync.Mutex
|
|
|
|
|
|
|
|
g.wg.Add(len(g.prevAllocs))
|
|
|
|
|
|
|
|
for _, alloc := range g.prevAllocs {
|
|
|
|
go func(ctx context.Context, alloc PrevAllocWatcher) {
|
|
|
|
defer g.wg.Done()
|
|
|
|
err := alloc.Wait(ctx)
|
|
|
|
if err != nil {
|
|
|
|
errmu.Lock()
|
|
|
|
merr.Errors = append(merr.Errors, err)
|
|
|
|
errmu.Unlock()
|
|
|
|
}
|
|
|
|
}(ctx, alloc)
|
|
|
|
}
|
|
|
|
|
|
|
|
g.wg.Wait()
|
|
|
|
|
|
|
|
// Check ctx.Err first, to avoid returning an mErr of ctx.Err from prevAlloc
|
|
|
|
// Wait routines.
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return merr.ErrorOrNil()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *groupPrevAllocWatcher) IsWaiting() bool {
|
|
|
|
g.waitingLock.RLock()
|
|
|
|
defer g.waitingLock.RUnlock()
|
|
|
|
|
|
|
|
return g.waiting
|
|
|
|
}
|