Documentation ¶
Overview ¶
Package loop supports the developer implementing the typical Go idiom for concurrent applications running in a loop in the background and doing a select on one or more channels. Stopping those loops or getting aware of internal errors requires extra efforts. The loop package helps to control this kind of goroutines.
type Printer struct { prints chan string loop loop.Loop } func NewPrinter(ctx context.Context) (*Printer, error) { p := &Printer{ ctx: ctx, prints: make(chan string), } l, err := loop.Go( p.worker, loop.WithContext(ctx), loop.WithFinalizer(func(err error) error { ... }) if err != nil { return nil, err } p.loop = l return p, nil } func (p *printer) worker(ctx context.Context) error { for { select { case <-ctx.Done(): return nil case str := <-p.prints: println(str) } }
The worker here now can be stopped with p.loop.Stop() returning a possible internal error. Also recovering of internal panics with a repairer function passed as option is possible. See the code examples.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Loop ¶
type Loop struct {
// contains filtered or unexported fields
}
Loop manages running for-select-loops in the background as goroutines in a controlled way. Users can get information about status and possible failure as well as control how to stop, restart, or recover via options.
type Option ¶
Option defines the signature of an option setting function.
func WithContext ¶
WithContext allows to pass a context for cancellation or timeout.
func WithFinalizer ¶
WithFinalizer sets a function for finalizing the work of a Loop.
func WithRepairer ¶
WithRepairer defines the panic handler of a loop.
type Repairer ¶
type Repairer func(reason interface{}) error
Repairer allows the loop goroutine to react on a panic during its work. Returning a nil the loop will be continued by calling the worker again.
Example ¶
ExampleRepairer demonstrates the usage of a repairer.
package main import ( "context" "fmt" "tideland.dev/go/together/loop" ) func main() { panics := make(chan string) // Sample loop worker. worker := func(ctx context.Context) error { for { select { case <-ctx.Done(): return nil case str := <-panics: panic(str) } } } // Repairer function checks the reasion. "never mind" will // be repaired, all others lead to an error. The repairer // is also responsable for fixing the owners state crashed // during panic. repairer := func(reason interface{}) error { why := reason.(string) if why == "never mind" { return nil } return fmt.Errorf("worker panic: %v", why) } l, err := loop.Go(worker, loop.WithRepairer(repairer)) if err != nil { panic(err) } l.Stop() }
Output:
type Worker ¶
Worker discribes the function running the loop.
Example ¶
ExampleWorker shows the usage of Loop with no repairer. The inner loop contains a select listening to the channel returned by Closer.Done(). Other channels are for the standard communication with the Loop.
package main import ( "context" "errors" "tideland.dev/go/together/loop" ) func main() { prints := make(chan string) ctx, cancel := context.WithCancel(context.Background()) // Sample loop worker. worker := func(ctx context.Context) error { for { select { case <-ctx.Done(): // We shall stop. return nil case str := <-prints: // Standard work of example loop. if str == "panic" { return errors.New("panic") } println(str) } } } l, err := loop.Go(worker, loop.WithContext(ctx)) if err != nil { panic(err) } prints <- "Hello" prints <- "World" // cancel() terminates the loop via the context. cancel() // Returned error must be nil in this example. if l.Err() != nil { panic(l.Err()) } }
Output: