loop

package
v0.0.0-...-6103407 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2021 License: BSD-3-Clause Imports: 5 Imported by: 1

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 Finalizer

type Finalizer func(err error) error

Finalizer is called with the final error if the backend loop terminates.

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.

func Go

func Go(worker Worker, options ...Option) (*Loop, error)

Go starts a loop running the given worker with the given options.

func (*Loop) Err

func (l *Loop) Err() error

Err returns information if the Loop has an error.

func (*Loop) Stop

func (l *Loop) Stop()

Stop terminates the Loop backend. It works asynchronous as the goroutine may need time for cleanup. Anyone wanting to be notified on state has to handle it in a Finalizer.

type Option

type Option func(loop *Loop) error

Option defines the signature of an option setting function.

func WithContext

func WithContext(ctx context.Context) Option

WithContext allows to pass a context for cancellation or timeout.

func WithFinalizer

func WithFinalizer(finalizer Finalizer) Option

WithFinalizer sets a function for finalizing the work of a Loop.

func WithRepairer

func WithRepairer(repairer Repairer) Option

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

type Worker func(ctx context.Context) error

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:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL