mirror of https://go.googlesource.com/go
264 lines
6.7 KiB
Go
264 lines
6.7 KiB
Go
// 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
|
|
}
|