go-director: github.com/relistan/go-director Index | Examples | Files

package director

import "github.com/relistan/go-director"

The package defines a single interface and a few implementations of the interface. The externalization of the loop flow control makes it easy to test the internal functions of background goroutines by, for instance, only running the loop once while under test.

The design is that any errors which need to be returned from the loop will be passed back on a channel whose implementation is left up to the individual Looper. Calling methods can wait on execution and for any resulting errors by calling the Wait() method on the Looper.

In this example, we are going to run a FreeLooper with 5 iterations. In the course of running, an error is generated, which the parent function captures and outputs. As a result of the error only 3 of the 5 iterations are completed and the output reflects this.

Code:

looper := NewFreeLooper(5, make(chan error))

runner := func(looper Looper) {
    x := 0
    looper.Loop(func() error {
        fmt.Println(x)
        x++
        if x == 3 {
            return errors.New("Uh oh")
        }
        return nil
    })
}

go runner(looper)
err := looper.Wait()

if err != nil {
    fmt.Printf("I got an error: %s\n", err.Error())
}

Output:

0
1
2
I got an error: Uh oh

Index

Examples

Package Files

director.go doc.go

Constants

const (
    FOREVER = -1
    ONCE    = 1
)

type FreeLooper Uses

type FreeLooper struct {
    Count    int
    DoneChan chan error
    // contains filtered or unexported fields
}

A FreeLooper is like a TimedLooper but doesn't wait between iterations.

func NewFreeLooper Uses

func NewFreeLooper(count int, done chan error) *FreeLooper

func (*FreeLooper) Done Uses

func (l *FreeLooper) Done(err error)

This is used internally, but can also be used by controlling routines to signal that a job is completed. The FreeLooper doesn's support its use outside the internals.

func (*FreeLooper) Loop Uses

func (l *FreeLooper) Loop(fn func() error)

func (*FreeLooper) Quit Uses

func (l *FreeLooper) Quit()

Quit() signals to the Looper to not run the next iteration and to call Done() and return as quickly as possible. It is does not intervene between iterations. It is a non-blocking operation.

func (*FreeLooper) Wait Uses

func (l *FreeLooper) Wait() error

type Looper Uses

type Looper interface {
    Loop(fn func() error)
    Wait() error
    Done(err error)
    Quit()
}

A Looper is used in place of a direct call to "for {}" and implements some controls over how the loop will be run. The Loop() function is the main call used by dependant routines. Common patterns like Quit and Done channels are easily implemented in a Looper.

type TimedLooper Uses

type TimedLooper struct {
    Count    int
    Interval time.Duration
    DoneChan chan error

    Immediate bool
    // contains filtered or unexported fields
}

A TimedLooper is a Looper that runs on a timed schedule, using a Timer underneath. It also implements Quit and Done channels to allow external routines to more easily control and synchronize the loop.

If you pass in a DoneChan at creation time, it will send a nil on the channel when the loop has completed successfully or an error if the loop resulted in an error condition.

In this example, we run a really fast TimedLooper for a fixed number of runs.

Code:

looper := NewTimedLooper(5, 1*time.Nanosecond, make(chan error))

runner := func(looper Looper) {
    x := 0
    looper.Loop(func() error {
        fmt.Println(x)
        x++
        return nil
    })
}

go runner(looper)
err := looper.Wait()
if err != nil {
    fmt.Printf("I got an error: %s\n", err.Error())
}

Output:

0
1
2
3
4

func NewImmediateTimedLooper Uses

func NewImmediateTimedLooper(count int, interval time.Duration, done chan error) *TimedLooper

Same as a TimedLooper, except it will execute an iteration of the loop immediately after calling on Loop() (as opposed to waiting until the tick)

func NewTimedLooper Uses

func NewTimedLooper(count int, interval time.Duration, done chan error) *TimedLooper

func (*TimedLooper) Done Uses

func (l *TimedLooper) Done(err error)

Signal a dependant routine that we're done with our work

func (*TimedLooper) Loop Uses

func (l *TimedLooper) Loop(fn func() error)

The main method of the Looper. This call takes a function with a single return value, an error. If the error is nil, the Looper will run the next iteration. If it's an error, it will not run the next iteration, will clean up any internals that need to be, and will invoke done().

func (*TimedLooper) Quit Uses

func (l *TimedLooper) Quit()

Quit() signals to the Looper to not run the next iteration and to call done() and return as quickly as possible. It is does not intervene between iterations.

In this example we run a really fast TimedLooper for a fixed number of runs, but we interrupt it with a Quit() call so it only completes one run.

Code:

looper := NewTimedLooper(5, 50*time.Millisecond, make(chan error))

runner := func(looper Looper) {
    x := 0
    looper.Loop(func() error {
        fmt.Println(x)
        x++
        return nil
    })
}

go runner(looper)
// Wait for one run to complete
time.Sleep(90 * time.Millisecond)
looper.Quit()
err := looper.Wait()
if err != nil {
    fmt.Printf("I got an error: %s\n", err.Error())
}

Output:

0

func (*TimedLooper) Wait Uses

func (l *TimedLooper) Wait() error

Package director imports 1 packages (graph) and is imported by 10 packages. Updated 2018-11-08. Refresh now. Tools for package owners.