1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
package lifecycle_test
import (
"context"
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/joshuarubin/lifecycle"
)
func Example() {
// This is only to ensure that the example completes
const timeout = 10 * time.Millisecond
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// At the top of your application
ctx = lifecycle.New(
ctx,
lifecycle.WithTimeout(30*time.Second), // optional
)
helloHandler := func(w http.ResponseWriter, req *http.Request) {
_, _ = io.WriteString(w, "Hello, world!\n")
}
mux := http.NewServeMux()
mux.HandleFunc("/hello", helloHandler)
srv := &http.Server{
Addr: ":8080",
Handler: mux,
}
var start, finish time.Time
lifecycle.GoErr(ctx, func() error {
start = time.Now()
return srv.ListenAndServe()
})
lifecycle.DeferErr(ctx, func() error {
finish = time.Now()
fmt.Println("shutting down http server")
// use a background context because we already have a timeout and when
// Defer funcs run, ctx is necessarily canceled.
return srv.Shutdown(context.Background())
})
// Any panics in Go or Defer funcs will be passed to the goroutine that Wait
// runs in, so it is possible to handle them like this
defer func() {
if r := recover(); r != nil {
panic(r) // example, you probably want to do something else
}
}()
// Then at the end of main(), or run() or however your application operates
//
// The returned err is the first non-nil error returned by any func
// registered with Go or Defer, otherwise nil.
if err := lifecycle.Wait(ctx); err != nil && err != context.DeadlineExceeded {
log.Fatal(err)
}
// This is just to show that the server will run for at least `timeout`
// before shutting down
if finish.Sub(start) < timeout {
log.Fatal("didn't wait long enough to shutdown")
}
// Output:
// shutting down http server
}
|