github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/internal/task/executor.go (about) 1 package task 2 3 import ( 4 "context" 5 "fmt" 6 "runtime/debug" 7 "sync" 8 9 "github.com/hashicorp/go-multierror" 10 11 "github.com/anchore/syft/internal/sbomsync" 12 "github.com/anchore/syft/syft/event/monitor" 13 "github.com/anchore/syft/syft/file" 14 ) 15 16 type Executor struct { 17 numWorkers int 18 tasks chan Task 19 } 20 21 func NewTaskExecutor(tasks []Task, numWorkers int) *Executor { 22 p := &Executor{ 23 numWorkers: numWorkers, 24 tasks: make(chan Task, len(tasks)), 25 } 26 27 for i := range tasks { 28 p.tasks <- tasks[i] 29 } 30 close(p.tasks) 31 32 return p 33 } 34 35 func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsync.Builder, prog *monitor.CatalogerTaskProgress) error { 36 var errs error 37 wg := &sync.WaitGroup{} 38 for i := 0; i < p.numWorkers; i++ { 39 wg.Add(1) 40 go func() { 41 defer wg.Done() 42 43 for { 44 tsk, ok := <-p.tasks 45 if !ok { 46 return 47 } 48 49 if err := runTaskSafely(ctx, tsk, resolver, s); err != nil { 50 errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err)) 51 prog.SetError(err) 52 } 53 prog.Increment() 54 } 55 }() 56 } 57 58 wg.Wait() 59 60 return errs 61 } 62 63 func runTaskSafely(ctx context.Context, t Task, resolver file.Resolver, s sbomsync.Builder) (err error) { 64 // handle individual cataloger panics 65 defer func() { 66 if e := recover(); e != nil { 67 err = fmt.Errorf("%v at:\n%s", e, string(debug.Stack())) 68 } 69 }() 70 71 return t.Execute(ctx, resolver, s) 72 }