summaryrefslogtreecommitdiffstats
path: root/src/context/example_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/context/example_test.go')
-rw-r--r--src/context/example_test.go263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/context/example_test.go b/src/context/example_test.go
new file mode 100644
index 0000000..03333b5
--- /dev/null
+++ b/src/context/example_test.go
@@ -0,0 +1,263 @@
+// 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
+}