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