Context and Cancellation
Cancellation Branch
A select can branch between a canceled context and a default path.
done channel
`ctx.Done()` is a channel that becomes ready after cancellation.
Cancellation Branch
cancellation_branch.go
Replay: real traced execution (multi-file project)
package main
import (
"context"
"fmt"
)
func main() {
var cancelNow = true
ctx, cancel := context.WithCancel(context.Background())
if cancelNow {
cancel()
}
status := ""
select {
case <-ctx.Done():
status = "canceled"
default:
status = "still running"
}
fmt.Println("cancelNow=", cancelNow)
fmt.Println("status=", status)
fmt.Println("hasError=", ctx.Err() != nil)
}
package main
import (
"context"
"fmt"
)
func main() {
var cancelNow = false
ctx, cancel := context.WithCancel(context.Background())
if cancelNow {
cancel()
}
status := ""
select {
case <-ctx.Done():
status = "canceled"
default:
status = "still running"
}
fmt.Println("cancelNow=", cancelNow)
fmt.Println("status=", status)
fmt.Println("hasError=", ctx.Err() != nil)
}
cancelNow ← true, ctx ← &context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:interface {}(nil)}, children:map[context.canceler]struct {}(nil), err:error(nil), cause:error(nil)}
8func main() {9 var cancelNow→ true = true //@cancelNow=false10 ctx→ &context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:interface {}(nil)}, children:map[context.canceler]struct {}(nil), err:error(nil), cause:error(nil)}, cancel→ (context.CancelFunc)(⟨addr A⟩) := context.WithCancel(context.Background())if cancelNow
12if cancelNowtrue {13 cancel()14}status ← ""
16 status→ "" := ""17 select {18 case <-ctx.Done():19 status→ "canceled" = "canceled"20 default:21 status = "still running"22 }2324 fmt.Println("cancelNow=", cancelNowtrue)25 fmt.Println("status=", status"canceled")26 fmt.Println("hasError=", ctx&context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:(chan struct {})(⟨addr B⟩)}, children:map[context.canceler]struct {}(nil), err:(*errors.errorString)(⟨addr C⟩), cause:(*errors.errorString)(⟨addr C⟩)}.Err() != nil)27}outputcancelNow= true status= canceled hasError= true
cancelNow ← false, ctx ← &context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:interface {}(nil)}, children:map[context.canceler]struct {}(nil), err:error(nil), cause:error(nil)}
8func main() {9 var cancelNow→ false = false10 ctx→ &context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:interface {}(nil)}, children:map[context.canceler]struct {}(nil), err:error(nil), cause:error(nil)}, cancel→ (context.CancelFunc)(⟨addr A⟩) := context.WithCancel(context.Background())1112 if cancelNow {13 cancel()14 }1516 status→ "" := ""17 select {18 case <-ctx.Done():19 status = "canceled"20 default:21 status = "still running"22 }2324 fmt.Println("cancelNow=", cancelNowfalse)25 fmt.Println("status=", status"still running")26 fmt.Println("hasError=", ctx&context.cancelCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, mu:sync.Mutex{state:0, sema:0x0}, done:atomic.Value{v:(chan struct {})(⟨addr B⟩)}, children:map[context.canceler]struct {}(nil), err:error(nil), cause:error(nil)}.Err() != nil)27}outputcancelNow= false status= still running hasError= false