Skip to content

Instantly share code, notes, and snippets.

@zucchinidev
Last active October 4, 2017 14:25
Show Gist options
  • Save zucchinidev/752caba07f921375403f0b6734316bb9 to your computer and use it in GitHub Desktop.
Save zucchinidev/752caba07f921375403f0b6734316bb9 to your computer and use it in GitHub Desktop.
Runner: The purpose of the runner package is to show how channels can be used to monitor the amount of time a program is running and terminate the program if it runs too long. This pattern is useful when developing a program that will be scheduled to run as a background task process.
package main
import (
"time"
"log"
"github.com/zucchinidev/runnerWork/runner"
"os"
)
const timeout = 3 * time.Second
func main() {
log.Println("Starting work")
runnerManager := runner.New(timeout)
runnerManager.AddTasks(createTask(), createTask(), createTask())
if err := runnerManager.StartTasks(); err != nil {
switch err {
case runner.ErrTimeout:
log.Println("Terminating due to timeout.")
os.Exit(1)
case runner.ErrInterrupt:
log.Println("Terminating due to system interrup.")
os.Exit(2)
}
}
log.Println("Process ended")
}
func createTask() func(int) {
return func(id int) {
log.Printf("Procesando tarea - Task #%d.", id)
time.Sleep(time.Duration(id) * time.Second)
}
}
package runner
import (
"os"
"time"
"errors"
"os/signal"
)
type Runner struct {
interruptReportChannel chan os.Signal
completeReportChannel chan error
timeoutReportChannel <-chan time.Time
tasks [] func(int)
}
var ErrTimeout = errors.New("received timeout")
var ErrInterrupt = errors.New("received interrupt")
func New(duration time.Duration) *Runner {
return &Runner{
interruptReportChannel: make(chan os.Signal, 1),
completeReportChannel: make(chan error),
timeoutReportChannel: time.After(duration),
}
}
func (runner *Runner) AddTasks(tasks ...func(int)) {
runner.tasks = append(runner.tasks, tasks...)
}
func (runner *Runner) StartTasks() error {
signal.Notify(runner.interruptReportChannel, os.Interrupt)
go func() {
runner.completeReportChannel <- runner.run()
}()
select {
case err := <-runner.completeReportChannel:
return err
case <-runner.timeoutReportChannel:
return ErrTimeout
}
}
func (runner *Runner) run() error {
for id, task := range runner.tasks {
if runner.gotInterrupt() {
return ErrInterrupt
}
task(id)
}
return nil
}
func (runner *Runner) gotInterrupt() bool {
select {
case <-runner.interruptReportChannel:
signal.Stop(runner.interruptReportChannel)
return true
default:
return false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment