blob: 03333b5ccae9599eb2d0cd179312d32830081e9b [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context_test
import (
"context"
"errors"
"fmt"
"net"
"sync"
"time"
)
var neverReady = make(chan struct{}) // never closed
// This example demonstrates the use of a cancelable context to prevent a
// goroutine leak. By the end of the example function, the goroutine started
// by gen will return without leaking.
func ExampleWithCancel() {
// gen generates integers in a separate goroutine and
// sends them to the returned channel.
// The callers of gen need to cancel the context once
// they are done consuming generated integers not to leak
// the internal goroutine started by gen.
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done():
return // returning not to leak the goroutine
case dst <- n:
n++
}
}
}()
return dst
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel when we are finished consuming integers
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
// Output:
// 1
// 2
// 3
// 4
// 5
}
// This example passes a context with an arbitrary deadline to tell a blocking
// function that it should abandon its work as soon as it gets to it.
func ExampleWithDeadline() {
d := time.Now().Add(shortDuration)
ctx, cancel := context.WithDeadline(context.Background(), d)
// Even though ctx will be expired, it is good practice to call its
// cancellation function in any case. Failure to do so may keep the
// context and its parent alive longer than necessary.
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
// Output:
// context deadline exceeded
}
// This example passes a context with a timeout to tell a blocking function that
// it should abandon its work after the timeout elapses.
func ExampleWithTimeout() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
// Output:
// context deadline exceeded
}
// This example demonstrates how a value can be passed to the context
// and also how to retrieve it if it exists.
func ExampleWithValue() {
type favContextKey string
f := func(ctx context.Context, k favContextKey) {
if v := ctx.Value(k); v != nil {
fmt.Println("found value:", v)
return
}
fmt.Println("key not found:", k)
}
k := favContextKey("language")
ctx := context.WithValue(context.Background(), k, "Go")
f(ctx, k)
f(ctx, favContextKey("color"))
// Output:
// found value: Go
// key not found: color
}
// This example uses AfterFunc to define a function which waits on a sync.Cond,
// stopping the wait when a context is canceled.
func ExampleAfterFunc_cond() {
waitOnCond := func(ctx context.Context, cond *sync.Cond, conditionMet func() bool) error {
stopf := context.AfterFunc(ctx, func() {
// We need to acquire cond.L here to be sure that the Broadcast
// below won't occur before the call to Wait, which would result
// in a missed signal (and deadlock).
cond.L.Lock()
defer cond.L.Unlock()
// If multiple goroutines are waiting on cond simultaneously,
// we need to make sure we wake up exactly this one.
// That means that we need to Broadcast to all of the goroutines,
// which will wake them all up.
//
// If there are N concurrent calls to waitOnCond, each of the goroutines
// will spuriously wake up O(N) other goroutines that aren't ready yet,
// so this will cause the overall CPU cost to be O(N²).
cond.Broadcast()
})
defer stopf()
// Since the wakeups are using Broadcast instead of Signal, this call to
// Wait may unblock due to some other goroutine's context becoming done,
// so to be sure that ctx is actually done we need to check it in a loop.
for !conditionMet() {
cond.Wait()
if ctx.Err() != nil {
return ctx.Err()
}
}
return nil
}
cond := sync.NewCond(new(sync.Mutex))
var wg sync.WaitGroup
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
cond.L.Lock()
defer cond.L.Unlock()
err := waitOnCond(ctx, cond, func() bool { return false })
fmt.Println(err)
}()
}
wg.Wait()
// Output:
// context deadline exceeded
// context deadline exceeded
// context deadline exceeded
// context deadline exceeded
}
// This example uses AfterFunc to define a function which reads from a net.Conn,
// stopping the read when a context is canceled.
func ExampleAfterFunc_connection() {
readFromConn := func(ctx context.Context, conn net.Conn, b []byte) (n int, err error) {
stopc := make(chan struct{})
stop := context.AfterFunc(ctx, func() {
conn.SetReadDeadline(time.Now())
close(stopc)
})
n, err = conn.Read(b)
if !stop() {
// The AfterFunc was started.
// Wait for it to complete, and reset the Conn's deadline.
<-stopc
conn.SetReadDeadline(time.Time{})
return n, ctx.Err()
}
return n, err
}
listener, err := net.Listen("tcp", ":0")
if err != nil {
fmt.Println(err)
return
}
defer listener.Close()
conn, err := net.Dial(listener.Addr().Network(), listener.Addr().String())
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
b := make([]byte, 1024)
_, err = readFromConn(ctx, conn, b)
fmt.Println(err)
// Output:
// context deadline exceeded
}
// This example uses AfterFunc to define a function which combines
// the cancellation signals of two Contexts.
func ExampleAfterFunc_merge() {
// mergeCancel returns a context that contains the values of ctx,
// and which is canceled when either ctx or cancelCtx is canceled.
mergeCancel := func(ctx, cancelCtx context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancelCause(ctx)
stop := context.AfterFunc(cancelCtx, func() {
cancel(context.Cause(cancelCtx))
})
return ctx, func() {
stop()
cancel(context.Canceled)
}
}
ctx1, cancel1 := context.WithCancelCause(context.Background())
defer cancel1(errors.New("ctx1 canceled"))
ctx2, cancel2 := context.WithCancelCause(context.Background())
mergedCtx, mergedCancel := mergeCancel(ctx1, ctx2)
defer mergedCancel()
cancel2(errors.New("ctx2 canceled"))
<-mergedCtx.Done()
fmt.Println(context.Cause(mergedCtx))
// Output:
// ctx2 canceled
}