killable

package module
v0.0.0-...-9380afb Latest Latest
Warning

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

Go to latest
Published: May 28, 2021 License: MIT Imports: 4 Imported by: 4

README

DEPRECATED - THIS PACKAGE IS BROKEN AND I'M NOT GOING TO FIX IT

Killable Build Status

A package for graceful shutdowns (inspired by tomb)

States

A Killable represents a group of goroutines. It goes through 3 stages:

  1. Alive - The goroutines are running
  2. Dying - The goroutines are being signaled to terminate
  3. Dead - All managed goroutines have terminated

There are two ways a Killable can enter the dying state.

  1. One of the managed goroutines returns a non nil error
  2. The Kill(error) method is invoked on the Killable.

Managed Goroutines

Goroutines managed by the Killable are started with killable.Go

k := killable.New()

go func() {
  <-k.Dying()
  fmt.Println("Dying")

  <-k.Dead()
  fmt.Println("Dead")
}()

killable.Go(k, func() error {
  time.Sleep(5 * time.Second)
  fmt.Println("Finished sleeping, i'll be dead soon")
  return nil
})

k.Kill(fmt.Errorf("it's time to die!"))
  • A Killable is not dead until all managed goroutines have returned.
  • If the goroutine returns a non nil error, the Killable starts dying.
  • If the Killable is already dying when the Go method is invoked, it does not run.

Defer

Defer is similar to the defer keyword.

func Connect(k killable.Killable) (*sql.DB, error) {

  db, err := sql.Open("foo", "bar")
  if err != nil {
    return nil, err
  }

  // clean up resources near instantiation
  killable.Defer(k, func() {
    db.Close()
  })

  return db, nil
}
  • Deferred methods are called once the killable is dead.
  • Deferred methods are invoked in the opposite order they were defined (lifo).

Linking

Killables can be linked to eachother in a parent/child relationship.

  • If a child is killed, the parent is also killed.
  • If the parent is killed, it kills all the children.
  • If the reason is ErrKillLocal, the parent ignores it.
  • The parent doesn't die until all the children are dead

func makeChild(d time.Duration) killable.Killable {
  k := killable.New()

  killable.Go(k, func() {

    // Sleep will immediately return ErrDying if the Killable
    // enters the dying state during the sleep
    // (see source to see how to implement similar methods)
    if err := killable.Sleep(k, d); err != nil {
      return err
    }

    return killable.ErrKill
  })

  return k
}

var (
  // children
  k1 = makeChild(4 * time.Second)
  k2 = makeChild(3 * time.Second)
  k3 = makeChild(2 * time.Second)

  // parent
  k4 = killable.New(k1, k2, k3)
)

killable.Defer(k4, func() {
  fmt.Println("All children are dead!")
})

go func() {
  <-k4.Dying()
  fmt.Println("Killing all children")
}()

See examples/ directory.

The methods like Defer, Go, Do, etc ... have been placed in the packages because the Killable type is meant to be embedded. The interface the Killable type exposes makes sense without understanding the killable package.

Context

Since Go 1.7, the many standard library functions have support for context.Context. The killable.Killable interface cannot be changed to conform to the context.Context interface because the semantics of the Err method. To get around this, killable.Killable has a .Context().

func DoQuery(k killable.Killable, db *sql.DB) error {
  _, err := db.ExecContext(k.Context(), "INSERT foo INTO bar")
  return err
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrDying is returned when some action was cancelled due
	// to the Dying channel closing
	ErrDying = errors.New("killable: dying")

	// ErrKill is returned from managed goroutines to
	// put the Killable into the dying state
	ErrKill = errors.New("killable: killed")

	// ErrKillLocal is returned from managed goroutines to
	// put the killable into the dying state but without
	// propagating it to parent Killables
	ErrKillLocal = errors.New("killable: local killed")

	// ErrStillAlive is returned by Err if the Killable isn't dying
	ErrStillAlive = errors.New("killable: still alive")
)

Functions

func Defer

func Defer(k Killable, fn func())

Defer invokes a callback once a Killable is dead. deferred functions will execute in the opposite order they were defined in if the Killable is already dead, the deferred will be called immediatly

func Do

func Do(k Killable, fn func() error) error

Do executes a function and retuns its error value. * If the Killable is marked as dying it will return immediatly with ErrDying. * The Killable will not be marked as dead until all calls to Do have returned.

func Err

func Err(k Killable) error

Err returns the Killable error If the Killable is alive, it returns ErrStillAlive

func Go

func Go(k Killable, fn func() error)

Go executes a function in a goroutine * If the function returns a non-nil error the Killable is killed using that error * The Killable will no be marked as dead until all calls to Go have returned

func IsAlive

func IsAlive(k Killable) bool

IsAlive returns true if the Killable isn't in the dead or dying states

func IsDead

func IsDead(k Killable) bool

IsDead true if the Killable is in the dead state

func IsDying

func IsDying(k Killable) bool

IsDying returns true if a Killable is in the dying or dead state

func Sleep

func Sleep(k Killable, d time.Duration) error

Sleep blocks for a specified duration If the Killable is marked as dying it will return immediatly with ErrDying

Types

type Killable

type Killable interface {
	// Dying is closed immediatly after Kill is called
	Dying() <-chan struct{}

	// Dead is closed after all executing functions have returned
	// These executing functions must have been started with Do or Go
	Dead() <-chan struct{}

	// Put the Killable into the dying state
	Kill(reason error)

	// Return the error passed to Kill
	// blocks until in dying state
	Err() error

	// Context returns a context.Context that's linked to the killable
	Context() context.Context
	// contains filtered or unexported methods
}

func New

func New(killables ...Killable) Killable

New creates a new Killable

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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