package executer import ( "errors" "os/exec" "time" ) var ErrTimeout = errors.New("operation timed out") type Job struct { Cmd *exec.Cmd Err chan error } type Worker struct { Jobs chan Job Timeout time.Duration } func NewJob(e *exec.Cmd) Job { return Job{ Cmd: e, Err: make(chan error), } } func NewWorker(timeout time.Duration) *Worker { w := &Worker{ Jobs: make(chan Job, 1), Timeout: timeout, } // start worker routine go w.run() return w } // runs in own goroutine func (e *Worker) run() { var done chan error if e.Timeout != 0 { done = make(chan error) defer close(done) } for j := range e.Jobs { if e.Timeout == 0 { // run synchronously j.Err <- j.Cmd.Run() } else { // run asyncly and timeout go func() { done <- j.Cmd.Run() }() select { case err := <-done: j.Err <- err case <-time.After(e.Timeout): j.Err <- j.Cmd.Process.Kill() j.Err <- ErrTimeout } } // signal done close(j.Err) } }