2020-05-11 20:57:46 +02:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information
|
|
|
|
|
|
|
|
package sync2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// WorkGroup implements waitable and closable group of workers.
|
2020-05-11 20:57:46 +02:00
|
|
|
type WorkGroup struct {
|
|
|
|
noCopy noCopy // nolint: structcheck
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
cond sync.Cond
|
|
|
|
|
|
|
|
initialized bool
|
|
|
|
closed bool
|
|
|
|
workers int
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// init initializes work group.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (group *WorkGroup) init() {
|
|
|
|
if !group.initialized {
|
|
|
|
group.cond.L = &group.mu
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go starts func and tracks the execution.
|
|
|
|
// Returns false when WorkGroup has been closed.
|
|
|
|
func (group *WorkGroup) Go(fn func()) bool {
|
|
|
|
if !group.Start() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
defer group.Done()
|
|
|
|
fn()
|
|
|
|
}()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Start returns true when work can be started.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (group *WorkGroup) Start() bool {
|
|
|
|
group.mu.Lock()
|
|
|
|
defer group.mu.Unlock()
|
|
|
|
|
|
|
|
group.init()
|
|
|
|
if group.closed {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
group.workers++
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Done finishes a pending work item.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (group *WorkGroup) Done() {
|
|
|
|
group.mu.Lock()
|
|
|
|
defer group.mu.Unlock()
|
|
|
|
|
|
|
|
group.workers--
|
|
|
|
if group.workers < 0 {
|
|
|
|
panic("worker count below zero")
|
|
|
|
}
|
|
|
|
if group.workers == 0 {
|
|
|
|
group.cond.Broadcast()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait waits for all workers to finish.
|
|
|
|
func (group *WorkGroup) Wait() {
|
|
|
|
group.mu.Lock()
|
|
|
|
defer group.mu.Unlock()
|
|
|
|
|
|
|
|
group.init()
|
|
|
|
|
|
|
|
for group.workers != 0 {
|
|
|
|
group.cond.Wait()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close prevents from new work being started.
|
|
|
|
func (group *WorkGroup) Close() {
|
|
|
|
group.mu.Lock()
|
|
|
|
defer group.mu.Unlock()
|
|
|
|
|
|
|
|
group.init()
|
|
|
|
group.closed = true
|
|
|
|
}
|