| 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 | 
 | } |