All checks were successful
Tests and linters / Run gofumpt (pull_request) Successful in 1m31s
DCO action / DCO (pull_request) Successful in 1m56s
Vulncheck / Vulncheck (pull_request) Successful in 2m37s
Pre-commit hooks / Pre-commit (pull_request) Successful in 2m49s
Build / Build Components (pull_request) Successful in 2m56s
Tests and linters / gopls check (pull_request) Successful in 3m15s
Tests and linters / Staticcheck (pull_request) Successful in 3m17s
Tests and linters / Lint (pull_request) Successful in 4m3s
Tests and linters / Tests (pull_request) Successful in 4m42s
Tests and linters / Tests with -race (pull_request) Successful in 6m18s
``` goos: linux goarch: amd64 pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine cpu: 12th Gen Intel(R) Core(TM) i5-1235U │ old.txt │ new2.txt │ │ sec/op │ sec/op vs base │ InhumeMultipart/objects=1-12 11.42m ± 1% 10.73m ± 0% -6.12% (p=0.000 n=10) InhumeMultipart/objects=10-12 113.51m ± 0% 11.00m ± 1% -90.31% (p=0.000 n=10) InhumeMultipart/objects=100-12 1135.41m ± 1% 22.36m ± 1% -98.03% (p=0.000 n=10) InhumeMultipart/objects=1000-12 11357.77m ± 0% 41.97m ± 24% -99.63% (p=0.000 n=10) InhumeMultipart/objects=10000-12 113250.7m ± 0% 273.9m ± 3% -99.76% (p=0.000 n=10) geomean 1.136 31.36m -97.24% ``` Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
110 lines
2.4 KiB
Go
110 lines
2.4 KiB
Go
package util
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/panjf2000/ants/v2"
|
|
)
|
|
|
|
// WorkerPool represents a tool to control
|
|
// the execution of go-routine pool.
|
|
type WorkerPool interface {
|
|
// Submit queues a function for execution
|
|
// in a separate routine.
|
|
//
|
|
// Implementation must return any error encountered
|
|
// that prevented the function from being queued.
|
|
Submit(func()) error
|
|
|
|
// Release releases worker pool resources. All `Submit` calls will
|
|
// finish with ErrPoolClosed. It doesn't wait until all submitted
|
|
// functions have returned so synchronization must be achieved
|
|
// via other means (e.g. sync.WaitGroup).
|
|
Release()
|
|
}
|
|
|
|
// pseudoWorkerPool represents a pseudo worker pool which executes the submitted job immediately in the caller's routine.
|
|
type pseudoWorkerPool struct {
|
|
closed atomic.Bool
|
|
}
|
|
|
|
// ErrPoolClosed is returned when submitting task to a closed pool.
|
|
var ErrPoolClosed = ants.ErrPoolClosed
|
|
|
|
// NewPseudoWorkerPool returns a new instance of a synchronous worker pool.
|
|
func NewPseudoWorkerPool() WorkerPool {
|
|
return &pseudoWorkerPool{}
|
|
}
|
|
|
|
// Submit executes the passed function immediately.
|
|
//
|
|
// Always returns nil.
|
|
func (p *pseudoWorkerPool) Submit(fn func()) error {
|
|
if p.closed.Load() {
|
|
return ErrPoolClosed
|
|
}
|
|
|
|
fn()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Release implements the WorkerPool interface.
|
|
func (p *pseudoWorkerPool) Release() {
|
|
p.closed.Store(true)
|
|
}
|
|
|
|
type WorkerTask func(ctx context.Context) error
|
|
|
|
type WorkerPoolSubmitError struct {
|
|
err error
|
|
}
|
|
|
|
func (e *WorkerPoolSubmitError) Error() string {
|
|
return e.err.Error()
|
|
}
|
|
|
|
func (e *WorkerPoolSubmitError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
// ExecuteWithWorkerPool runs tasks in parallel using a pool and waits for all
|
|
// tasks to be complete.
|
|
//
|
|
// Returns [WorkerPoolSubmitError] when it couldn't submit a task.
|
|
func ExecuteWithWorkerPool(ctx context.Context, pool WorkerPool, tasks []WorkerTask) error {
|
|
taskCtx, taskCancel := context.WithCancelCause(ctx)
|
|
defer taskCancel(nil)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
loop:
|
|
for _, task := range tasks {
|
|
select {
|
|
case <-ctx.Done():
|
|
taskCancel(context.Cause(ctx))
|
|
break loop
|
|
default:
|
|
}
|
|
|
|
wg.Add(1)
|
|
if err := pool.Submit(func() {
|
|
defer wg.Done()
|
|
|
|
if err := task(taskCtx); err != nil {
|
|
taskCancel(err)
|
|
}
|
|
}); err != nil {
|
|
wg.Done()
|
|
taskCancel(err)
|
|
wg.Wait()
|
|
|
|
return &WorkerPoolSubmitError{err}
|
|
}
|
|
}
|
|
wg.Wait()
|
|
|
|
return context.Cause(taskCtx)
|
|
}
|