blob: a6bb3aa4727159c02994c3cd77d4ac8a8c9277c3 [file] [log] [blame]
package combinectx_test
import (
"context"
"errors"
"fmt"
"time"
"source.monogon.dev/metropolis/pkg/combinectx"
)
// ExampleCombine shows how to combine two contexts for use with a contextful
// method.
func ExampleCombine() {
// Let's say you're trying to combine two different contexts: the first one is
// some long-term local worker context, while the second is a context from some
// incoming request.
ctxA, cancelA := context.WithCancel(context.Background())
ctxB, cancelB := context.WithTimeout(context.Background(), time.Millisecond * 100)
defer cancelA()
defer cancelB()
// doIO is some contextful, black box IO-heavy function. You want it to return
// early when either the long-term context or the short-term request context
// are Done().
doIO := func(ctx context.Context) (string, error) {
t := time.NewTicker(time.Second)
defer t.Stop()
select {
case <-ctx.Done():
return "", ctx.Err()
case <-t.C:
return "successfully reticulated splines", nil
}
}
// Combine the two given contexts into one...
ctx := combinectx.Combine(ctxA, ctxB)
// ... and call doIO with it.
v, err := doIO(ctx)
if err == nil {
fmt.Printf("doIO success: %v\n", v)
return
}
fmt.Printf("doIO failed: %v\n", err)
// The returned error will always be equal to the combined context's Err() call
// if the error is due to the combined context being Done().
if err == ctx.Err() {
fmt.Printf("doIO err == ctx.Err()\n")
}
// The returned error will pass any errors.Is(err, context....) checks. This
// ensures compatibility with blackbox code that performs special actions on
// the given context.
if errors.Is(err, context.DeadlineExceeded) {
fmt.Printf("doIO err is context.DeadlineExceeded\n")
}
// The returned error can be inspected by converting it to a *Error and calling
// .First()/.Second()/.Unwrap(). This lets the caller figure out which of the
// parent contexts caused the combined context to expires.
cerr := &combinectx.Error{}
if errors.As(err, &cerr) {
fmt.Printf("doIO err is *combinectx.Error\n")
fmt.Printf("doIO first failed: %v, second failed: %v\n", cerr.First(), cerr.Second())
}
// Output:
// doIO failed: context deadline exceeded
// doIO err == ctx.Err()
// doIO err is context.DeadlineExceeded
// doIO err is *combinectx.Error
// doIO first failed: false, second failed: true
}