mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
82 lines
1.9 KiB
Go
82 lines
1.9 KiB
Go
// (c) 2020-2023, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
type BoundedWorkers struct {
|
|
workerCount atomic.Int32
|
|
workerSpawner chan struct{}
|
|
outstandingWorkers sync.WaitGroup
|
|
|
|
work chan func()
|
|
workClose sync.Once
|
|
}
|
|
|
|
// NewBoundedWorkers returns an instance of [BoundedWorkers] that
|
|
// will spawn up to [max] goroutines.
|
|
func NewBoundedWorkers(max int) *BoundedWorkers {
|
|
return &BoundedWorkers{
|
|
workerSpawner: make(chan struct{}, max),
|
|
work: make(chan func()),
|
|
}
|
|
}
|
|
|
|
// startWorker creates a new goroutine to execute [f] immediately and then keeps the goroutine
|
|
// alive to continue executing new work.
|
|
func (b *BoundedWorkers) startWorker(f func()) {
|
|
b.workerCount.Add(1)
|
|
b.outstandingWorkers.Add(1)
|
|
|
|
go func() {
|
|
defer b.outstandingWorkers.Done()
|
|
|
|
if f != nil {
|
|
f()
|
|
}
|
|
for f := range b.work {
|
|
f()
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Execute the given function on an existing goroutine waiting for more work, a new goroutine,
|
|
// or return if the context is canceled.
|
|
//
|
|
// Execute must not be called after Wait, otherwise it might panic.
|
|
func (b *BoundedWorkers) Execute(f func()) {
|
|
// Ensure we feed idle workers first
|
|
select {
|
|
case b.work <- f:
|
|
return
|
|
default:
|
|
}
|
|
|
|
// Fallback to waiting for an idle worker or allocating
|
|
// a new worker (if we aren't yet at max concurrency)
|
|
select {
|
|
case b.work <- f:
|
|
case b.workerSpawner <- struct{}{}:
|
|
b.startWorker(f)
|
|
}
|
|
}
|
|
|
|
// Wait returns after all enqueued work finishes and all goroutines to exit.
|
|
// Wait returns the number of workers that were spawned during the run.
|
|
//
|
|
// Wait can only be called after ALL calls to [Execute] have returned.
|
|
//
|
|
// It is safe to call Wait multiple times but not safe to call [Execute]
|
|
// after [Wait] has been called.
|
|
func (b *BoundedWorkers) Wait() int {
|
|
b.workClose.Do(func() {
|
|
close(b.work)
|
|
})
|
|
b.outstandingWorkers.Wait()
|
|
return int(b.workerCount.Load())
|
|
}
|