diff options
Diffstat (limited to 'src/context/example_test.go')
-rw-r--r-- | src/context/example_test.go | 263 |
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 +} |