Initial commit
This commit is contained in:
parent
20a94545c7
commit
f50dc198ff
3 changed files with 131 additions and 0 deletions
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# go-atomic
|
||||
Useful custom atomic-safe [Go](https://golang.org/) structures.
|
58
workergroup.go
Normal file
58
workergroup.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package workergroup
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const waitTime = time.Nanosecond * 10
|
||||
|
||||
// WorkerGroup acts as a sync.WaitGroup but with a max worker count.
|
||||
type WorkerGroup struct {
|
||||
maxWorkers uint32
|
||||
workers uint32
|
||||
}
|
||||
|
||||
// MaxWorkers sets max workers to `workers` or returns current setting if < 0
|
||||
func (wg *WorkerGroup) MaxWorkers(workers uint32) uint32 {
|
||||
if workers == 0 {
|
||||
// update max workers to GOMAXPROCS if set to 0
|
||||
atomic.CompareAndSwapUint32(&wg.maxWorkers, 0, uint32(runtime.GOMAXPROCS(0)))
|
||||
|
||||
return atomic.LoadUint32(&wg.maxWorkers)
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&wg.maxWorkers, workers)
|
||||
|
||||
return workers
|
||||
}
|
||||
|
||||
// Add delta to the worker count
|
||||
func (wg *WorkerGroup) Add(delta uint32) {
|
||||
// update max workers to GOMAXPROCES if set to 0
|
||||
atomic.CompareAndSwapUint32(&wg.maxWorkers, 0, uint32(runtime.GOMAXPROCS(0)))
|
||||
|
||||
var oldCount uint32
|
||||
for oldCount = atomic.LoadUint32(&wg.workers); oldCount+delta > atomic.LoadUint32(&wg.maxWorkers); oldCount = atomic.LoadUint32(&wg.workers) {
|
||||
time.Sleep(waitTime)
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapUint32(&wg.workers, oldCount, oldCount+delta) {
|
||||
return
|
||||
}
|
||||
|
||||
wg.Add(delta)
|
||||
}
|
||||
|
||||
// Done decrement the worker counter
|
||||
func (wg *WorkerGroup) Done() {
|
||||
atomic.AddUint32(&wg.workers, ^uint32(0))
|
||||
}
|
||||
|
||||
// Wait until worker count is zero
|
||||
func (wg *WorkerGroup) Wait() {
|
||||
for atomic.LoadUint32(&wg.workers) != 0 {
|
||||
time.Sleep(waitTime)
|
||||
}
|
||||
}
|
71
workergroup_test.go
Normal file
71
workergroup_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package workergroup
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWorkerGroupMaxWorkers(t *testing.T) {
|
||||
wg := WorkerGroup{}
|
||||
|
||||
if wg.MaxWorkers(0) != uint32(runtime.GOMAXPROCS(0)) {
|
||||
t.Error("Default max workers should be GOMAXPROCS got", wg.MaxWorkers(0))
|
||||
}
|
||||
|
||||
if wg.MaxWorkers(3) != 3 {
|
||||
t.Error("Failed to set max workers to 3")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerGroupAdd(t *testing.T) {
|
||||
wg := WorkerGroup{}
|
||||
|
||||
wg.MaxWorkers(3)
|
||||
|
||||
wg.Add(1)
|
||||
wg.Add(1)
|
||||
wg.Add(1)
|
||||
|
||||
waitChan := make(chan time.Time, 1)
|
||||
|
||||
start := time.Now()
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
waitChan <- time.Now()
|
||||
}()
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
|
||||
wg.Done()
|
||||
stop := <-waitChan
|
||||
|
||||
if time.Duration(stop.Sub(start).Nanoseconds()) < (time.Millisecond * 50) {
|
||||
t.Error("wait channel should have waited at least 50ms, waited", stop.Sub(start))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerGroupWait(t *testing.T) {
|
||||
wg := WorkerGroup{}
|
||||
|
||||
wg.MaxWorkers(3)
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
time.Sleep(time.Millisecond)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// for safety
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
time.Sleep(time.Millisecond)
|
||||
if atomic.LoadUint32(&wg.workers) != 0 {
|
||||
t.Error("Failed to wait until workers was actually 0")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue