Last active
October 4, 2017 14:25
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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