blob: a6bb3aa4727159c02994c3cd77d4ac8a8c9277c3 [file] [log] [blame]
Serge Bazanski4166a712021-06-07 21:58:54 +02001package combinectx_test
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "time"
8
9 "source.monogon.dev/metropolis/pkg/combinectx"
10)
11
12// ExampleCombine shows how to combine two contexts for use with a contextful
13// method.
14func ExampleCombine() {
15 // Let's say you're trying to combine two different contexts: the first one is
16 // some long-term local worker context, while the second is a context from some
17 // incoming request.
18 ctxA, cancelA := context.WithCancel(context.Background())
19 ctxB, cancelB := context.WithTimeout(context.Background(), time.Millisecond * 100)
20 defer cancelA()
21 defer cancelB()
22
23 // doIO is some contextful, black box IO-heavy function. You want it to return
24 // early when either the long-term context or the short-term request context
25 // are Done().
26 doIO := func(ctx context.Context) (string, error) {
27 t := time.NewTicker(time.Second)
28 defer t.Stop()
29
30 select {
31 case <-ctx.Done():
32 return "", ctx.Err()
33 case <-t.C:
34 return "successfully reticulated splines", nil
35 }
36 }
37
38 // Combine the two given contexts into one...
39 ctx := combinectx.Combine(ctxA, ctxB)
40 // ... and call doIO with it.
41 v, err := doIO(ctx)
42 if err == nil {
43 fmt.Printf("doIO success: %v\n", v)
44 return
45 }
46
47 fmt.Printf("doIO failed: %v\n", err)
48
49 // The returned error will always be equal to the combined context's Err() call
50 // if the error is due to the combined context being Done().
51 if err == ctx.Err() {
52 fmt.Printf("doIO err == ctx.Err()\n")
53 }
54
55 // The returned error will pass any errors.Is(err, context....) checks. This
56 // ensures compatibility with blackbox code that performs special actions on
57 // the given context.
58 if errors.Is(err, context.DeadlineExceeded) {
59 fmt.Printf("doIO err is context.DeadlineExceeded\n")
60 }
61
62 // The returned error can be inspected by converting it to a *Error and calling
63 // .First()/.Second()/.Unwrap(). This lets the caller figure out which of the
64 // parent contexts caused the combined context to expires.
65 cerr := &combinectx.Error{}
66 if errors.As(err, &cerr) {
67 fmt.Printf("doIO err is *combinectx.Error\n")
68 fmt.Printf("doIO first failed: %v, second failed: %v\n", cerr.First(), cerr.Second())
69 }
70
71 // Output:
72 // doIO failed: context deadline exceeded
73 // doIO err == ctx.Err()
74 // doIO err is context.DeadlineExceeded
75 // doIO err is *combinectx.Error
76 // doIO first failed: false, second failed: true
77}