mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
77 lines
2.3 KiB
Go
77 lines
2.3 KiB
Go
package util
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// ConcurrentTask is a task to execute in a go-routine
|
|
type ConcurrentTask struct {
|
|
ToRun func(errChannel chan error)
|
|
}
|
|
|
|
// run encapsulates the work to be done by calling the ToRun function
|
|
func (ct ConcurrentTask) run(errChannel chan error, wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
ct.ToRun(errChannel)
|
|
}
|
|
|
|
// ConcurrentTasks records tasks to be run concurrently with go-routines
|
|
type ConcurrentTasks struct {
|
|
tasks []ConcurrentTask
|
|
}
|
|
|
|
// NewConcurrentTasks creates a new ConcurrentTasks instance, dimensioned to accept at least the specified number of tasks
|
|
func NewConcurrentTasks(taskNumber int) *ConcurrentTasks {
|
|
return &ConcurrentTasks{tasks: make([]ConcurrentTask, 0, taskNumber)}
|
|
}
|
|
|
|
// Add adds the specified ConcurrentTask to the list of tasks to be run concurrently
|
|
func (ct *ConcurrentTasks) Add(task ConcurrentTask) {
|
|
if len(ct.tasks) == 0 {
|
|
ct.tasks = make([]ConcurrentTask, 0, 7)
|
|
}
|
|
ct.tasks = append(ct.tasks, task)
|
|
}
|
|
|
|
// Run concurrently runs the added tasks failing on the first error
|
|
// Based on https://garrypolley.com/2016/02/10/golang-routines-errors/
|
|
func (ct *ConcurrentTasks) Run() error {
|
|
var wg sync.WaitGroup
|
|
finished := make(chan bool, 1) // this along with wg.Wait() is why the error handling works and doesn't deadlock
|
|
errChannel := make(chan error)
|
|
|
|
for _, task := range ct.tasks {
|
|
wg.Add(1)
|
|
go task.run(errChannel, &wg)
|
|
}
|
|
|
|
// Put the wait group in a go routine.
|
|
// By putting the wait group in the go routine we ensure either all pass
|
|
// and we close the "finished" channel or we wait forever for the wait group
|
|
// to finish.
|
|
//
|
|
// Waiting forever is okay because of the blocking select below.
|
|
go func() {
|
|
wg.Wait()
|
|
close(finished)
|
|
}()
|
|
|
|
// This select will block until one of the two channels returns a value.
|
|
// This means on the first failure in the go routines above the errChannel will release a
|
|
// value first. Because there is a "return" statement in the err check this function will
|
|
// exit when an error occurs.
|
|
//
|
|
// Due to the blocking on wg.Wait() the finished channel will not get a value unless all
|
|
// the go routines before were successful because not all the wg.Done() calls would have
|
|
// happened.
|
|
select {
|
|
case <-finished:
|
|
case err := <-errChannel:
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|