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