On Friday we looked at the context.Context interface as a whole. Now let’s go back and look at each method in greater detail.
type Context
type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool)
So there are two general cases here:
deadline, ok := ctx.Deadline()
if ok {
// The context has a deadline
fmt.Println("This context will timeout at %v", deadline)
} else {
// The context has no deadline, `deadline`'s value is meaningless
fmt.Println("This context has no deadline")
}
You can see it in action.
Seems pretty simple, right? It is.
However, there are a couple of subtleties at play that are worth calling out:
-
As already discussed, a call to
context.WithTimeoutactually sets a deadline. So callingctx.Deadline()on a context with a timeout (as done in the above playground link) will return a concrete time. -
Also as previously mentioned in the documentation for the relevant functions, in cases where more than one context in a context chain include a deadline (or timeout), only the earliest is effective. So calling
ctx.Deadline()may return a deadline earlier than some deadline or timeout you set, if the parent context already has an earlier deadline:
parent, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
/* ... somewhere later ... */
ctx, cancel := context.WithTimeout(parent, 30 * time.Second)
defer cancel()
deadline, _ := ctx.Deadline() // Here `deadline`, will reflect a 5-second timeout, not 30.