• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.circleci/H15-Apr-2019-

.gitignoreH A D15-Apr-201924

LICENSEH A D15-Apr-20191.1 KiB

README.mdH A D15-Apr-20193.2 KiB

example_test.goH A D15-Apr-20191.8 KiB

go.modH A D15-Apr-2019111

go.sumH A D15-Apr-2019209

lifecycle.goH A D15-Apr-20195.8 KiB

lifecycle_test.goH A D15-Apr-20198.5 KiB

options.goH A D15-Apr-2019855

README.md

1# lifecycle
2
3[![GoDoc](https://godoc.org/github.com/joshuarubin/lifecycle?status.svg)](https://godoc.org/github.com/joshuarubin/lifecycle) [![Go Report Card](https://goreportcard.com/badge/github.com/joshuarubin/lifecycle)](https://goreportcard.com/report/github.com/joshuarubin/lifecycle) [![codecov](https://codecov.io/gh/joshuarubin/lifecycle/branch/master/graph/badge.svg)](https://codecov.io/gh/joshuarubin/lifecycle) [![CircleCI](https://circleci.com/gh/joshuarubin/lifecycle.svg?style=svg)](https://circleci.com/gh/joshuarubin/lifecycle)
4
5## Overview
6
7`lifecycle` helps manage goroutines at the application level. `context.Context` has been great for propagating cancellation signals, but not for getting any feedback about _when_ goroutines actually finish.
8
9This package works with `context.Context` to ensure that applications don't quit before their goroutines do.
10
11The semantics work similarly to the `go` (`lifecycle.Go`) and `defer` (`lifecycle.Defer`) keywords as well as `sync.WaitGroup.Wait` (`lifecycle.Wait`). Additionally, there are `lifecycle.GoErr` and `lifecycle.DeferErr` which only differ in that they take funcs that return errors.
12
13`lifecycle.Wait` will block until one of the following happens:
14
15- all funcs registered with `Go` complete successfully then all funcs registered with `Defer` complete successfully
16- a func registered with `Go` returns an error, immediately canceling `ctx` and triggering `Defer` funcs to run. Once all `Go` and `Defer` funcs complete, `Wait` will return the error
17- a signal (by default `SIGINT` and `SIGTERM`, but configurable with `WithSignals`) is received, immediately canceling `ctx` and triggering `Defer` funcs to run. Once all `Go` and `Defer` funcs complete, `Wait` will return `ErrSignal`
18- a func registered with `Go` or `Defer` panics. the panic will be propagated to the goroutine that `Wait` runs in. there is no attempt, in case of a panic, to manage the state within the `lifecycle` package.
19
20## Example
21
22Here is an example that shows how `lifecycle` could work with an `http.Server`:
23
24```go
25// At the top of your application
26ctx := lifecycle.New(
27    context.Background(),
28    lifecycle.WithTimeout(30*time.Second), // optional
29)
30
31helloHandler := func(w http.ResponseWriter, req *http.Request) {
32    _, _ = io.WriteString(w, "Hello, world!\n")
33}
34
35mux := http.NewServeMux()
36mux.HandleFunc("/hello", helloHandler)
37
38srv := &http.Server{
39    Addr:    ":8080",
40    Handler: mux,
41}
42
43lifecycle.GoErr(ctx, srv.ListenAndServe)
44
45lifecycle.DeferErr(ctx, func() error {
46    // use a background context because we already have a timeout and when
47    // Defer funcs run, ctx is necessarily canceled.
48    return srv.Shutdown(context.Background())
49})
50
51// Any panics in Go or Defer funcs will be passed to the goroutine that Wait
52// runs in, so it is possible to handle them like this
53defer func() {
54    if r := recover(); r != nil {
55        panic(r) // example, you probably want to do something else
56    }
57}()
58
59// Then at the end of main(), or run() or however your application operates
60//
61// The returned err is the first non-nil error returned by any func
62// registered with Go or Defer, otherwise nil.
63if err := lifecycle.Wait(ctx); err != nil {
64    log.Fatal(err)
65}
66```
67