diff --git a/.gitignore b/.gitignore index 6503950..f5f3325 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ result* /tests/*/gomod2nix.toml /tests/*/go.mod +.idea diff --git a/internal/generate/generate.go b/internal/generate/generate.go index 12db73b..8980d0f 100644 --- a/internal/generate/generate.go +++ b/internal/generate/generate.go @@ -96,7 +96,7 @@ func ImportPkgs(directory string, numWorkers int) error { return err } - executor := lib.NewParallellExecutor(numWorkers) + executor := lib.NewParallelExecutor(numWorkers) for _, dl := range modDownloads { dl := dl executor.Add(func() error { @@ -146,7 +146,7 @@ func GeneratePkgs(directory string, goMod2NixPath string, numWorkers int) ([]*sc return nil, err } - executor := lib.NewParallellExecutor(numWorkers) + executor := lib.NewParallelExecutor(numWorkers) var mux sync.Mutex cache := schema.ReadCache(goMod2NixPath) diff --git a/internal/lib/executor.go b/internal/lib/executor.go index 7b605d3..1d1e51c 100644 --- a/internal/lib/executor.go +++ b/internal/lib/executor.go @@ -4,8 +4,8 @@ import ( "sync" ) -// ParallellExecutor - Execute callback functions in parallell -type ParallellExecutor struct { +// ParallelExecutor - Execute callback functions in parallel +type ParallelExecutor struct { errChan chan error wg *sync.WaitGroup mux *sync.Mutex @@ -16,8 +16,8 @@ type ParallellExecutor struct { done bool } -func NewParallellExecutor(maxWorkers int) *ParallellExecutor { - return &ParallellExecutor{ +func NewParallelExecutor(maxWorkers int) *ParallelExecutor { + return &ParallelExecutor{ errChan: make(chan error), mux: new(sync.Mutex), wg: new(sync.WaitGroup), @@ -28,12 +28,11 @@ func NewParallellExecutor(maxWorkers int) *ParallellExecutor { } } -func (e *ParallellExecutor) Add(fn func() error) { +func (e *ParallelExecutor) Add(fn func() error) { e.wg.Add(1) - e.guard <- struct{}{} // Block until a worker is available - go func() { + e.guard <- struct{}{} // Block until a worker is available defer e.wg.Done() defer func() { <-e.guard @@ -46,7 +45,7 @@ func (e *ParallellExecutor) Add(fn func() error) { }() } -func (e *ParallellExecutor) Wait() error { +func (e *ParallelExecutor) Wait() error { e.mux.Lock() defer e.mux.Unlock() diff --git a/internal/lib/executor_test.go b/internal/lib/executor_test.go new file mode 100644 index 0000000..d589f0d --- /dev/null +++ b/internal/lib/executor_test.go @@ -0,0 +1,36 @@ +package lib + +import ( + "errors" + "testing" + "time" +) + +// TestParallelExecutor_fnAlwaysErrors ensures that the executor does not block +// forever when there are more erroring functions than workers. This is a +// regression test. +func TestParallelExecutor_fnAlwaysErrors(t *testing.T) { + const maxWorkers = 1 + executor := NewParallelExecutor(1) + + for i := 0; i < maxWorkers+1; i++ { + executor.Add(func() error { + return errors.New("testerror") + }) + } + + errCh := make(chan error) + go func() { + defer close(errCh) + errCh <- executor.Wait() + }() + + select { + case err := <-errCh: + if err == nil { + t.Error("Expected error, got nil") + } + case <-time.After(10 * time.Second): + t.Error("Timed out waiting for executor to finish: deadlock") + } +}