package main

import (
	"context"
	"flag"
	"log"
	"os/exec"
	"sync"
	"sync/atomic"
)

func main() {
	var (
		workers = flag.Int("workers", 10, "worker count")
		cmdIter = flag.Int("cmd_iter", 10000, "iterations per command worker")
		cmd     = flag.String("cmd", "/bin/true", "command to run")
	)
	flag.Parse()

	ctx := context.Background()

	var wg sync.WaitGroup
	ctx, cancel := context.WithCancel(ctx)
	errs := make(chan error, 1)
	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for j := 0; j < *cmdIter; j++ {
				sink.Store(make([]*byte, 1<<20))

				err := doCmd(ctx, *cmd)

				if err != nil {
					cancel()
					select {
					case errs <- err:
					default:
					}
					return
				}
			}
		}()
	}

	wg.Wait()
	cancel()
	close(errs)
	err := <-errs
	if err != nil {
		log.Fatalf("%v", err)
	}
}

var sink atomic.Value

func doCmd(ctx context.Context, command string) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	cmd := exec.Command(command)

	err := cmd.Start()
	if err != nil {
		return err
	}

	done := make(chan error, 1)
	go func() {
		err := cmd.Wait()
		done <- err
	}()

	select {
	case <-ctx.Done():
		err := cmd.Process.Kill()
		if err != nil {
			return err
		}
		return ctx.Err()
	case err = <-done:
		return err
	}
}
