Source File
context.go
Belonging Package
context
// Copyright 2014 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 defines the Context type, which carries deadlines,// cancellation signals, and other request-scoped values across API boundaries// and between processes.//// Incoming requests to a server should create a [Context], and outgoing// calls to servers should accept a Context. The chain of function// calls between them must propagate the Context, optionally replacing// it with a derived Context created using [WithCancel], [WithDeadline],// [WithTimeout], or [WithValue].//// A Context may be canceled to indicate that work done on its behalf should stop.// A Context with a deadline is canceled after the deadline passes.// When a Context is canceled, all Contexts derived from it are also canceled.//// The [WithCancel], [WithDeadline], and [WithTimeout] functions take a// Context (the parent) and return a derived Context (the child) and a// [CancelFunc]. Calling the CancelFunc directly cancels the child and its// children, removes the parent's reference to the child, and stops// any associated timers. Failing to call the CancelFunc leaks the// child and its children until the parent is canceled. The go vet tool// checks that CancelFuncs are used on all control-flow paths.//// The [WithCancelCause], [WithDeadlineCause], and [WithTimeoutCause] functions// return a [CancelCauseFunc], which takes an error and records it as// the cancellation cause. Calling [Cause] on the canceled context// or any of its children retrieves the cause. If no cause is specified,// Cause(ctx) returns the same value as ctx.Err().//// Programs that use Contexts should follow these rules to keep interfaces// consistent across packages and enable static analysis tools to check context// propagation://// Do not store Contexts inside a struct type; instead, pass a Context// explicitly to each function that needs it. This is discussed further in// https://go.dev/blog/context-and-structs. The Context should be the first// parameter, typically named ctx://// func DoSomething(ctx context.Context, arg Arg) error {// // ... use ctx ...// }//// Do not pass a nil [Context], even if a function permits it. Pass [context.TODO]// if you are unsure about which Context to use.//// Use context Values only for request-scoped data that transits processes and// APIs, not for passing optional parameters to functions.//// The same Context may be passed to functions running in different goroutines;// Contexts are safe for simultaneous use by multiple goroutines.//// See https://go.dev/blog/context for example code for a server that uses// Contexts.package contextimport ()// A Context carries a deadline, a cancellation signal, and other values across// API boundaries.//// Context's methods may be called by multiple goroutines simultaneously.type Context interface {// Deadline returns the time when work done on behalf of this context// should be canceled. Deadline returns ok==false when no deadline is// set. Successive calls to Deadline return the same results.Deadline() (deadline time.Time, ok bool)// Done returns a channel that's closed when work done on behalf of this// context should be canceled. Done may return nil if this context can// never be canceled. Successive calls to Done return the same value.// The close of the Done channel may happen asynchronously,// after the cancel function returns.//// WithCancel arranges for Done to be closed when cancel is called;// WithDeadline arranges for Done to be closed when the deadline// expires; WithTimeout arranges for Done to be closed when the timeout// elapses.//// Done is provided for use in select statements://// // Stream generates values with DoSomething and sends them to out// // until DoSomething returns an error or ctx.Done is closed.// func Stream(ctx context.Context, out chan<- Value) error {// for {// v, err := DoSomething(ctx)// if err != nil {// return err// }// select {// case <-ctx.Done():// return ctx.Err()// case out <- v:// }// }// }//// See https://blog.golang.org/pipelines for more examples of how to use// a Done channel for cancellation.Done() <-chan struct{}// If Done is not yet closed, Err returns nil.// If Done is closed, Err returns a non-nil error explaining why:// DeadlineExceeded if the context's deadline passed,// or Canceled if the context was canceled for some other reason.// After Err returns a non-nil error, successive calls to Err return the same error.Err() error// Value returns the value associated with this context for key, or nil// if no value is associated with key. Successive calls to Value with// the same key returns the same result.//// Use context values only for request-scoped data that transits// processes and API boundaries, not for passing optional parameters to// functions.//// A key identifies a specific value in a Context. Functions that wish// to store values in Context typically allocate a key in a global// variable then use that key as the argument to context.WithValue and// Context.Value. A key can be any type that supports equality;// packages should define keys as an unexported type to avoid// collisions.//// Packages that define a Context key should provide type-safe accessors// for the values stored using that key://// // Package user defines a User type that's stored in Contexts.// package user//// import "context"//// // User is the type of value stored in the Contexts.// type User struct {...}//// // key is an unexported type for keys defined in this package.// // This prevents collisions with keys defined in other packages.// type key int//// // userKey is the key for user.User values in Contexts. It is// // unexported; clients use user.NewContext and user.FromContext// // instead of using this key directly.// var userKey key//// // NewContext returns a new Context that carries value u.// func NewContext(ctx context.Context, u *User) context.Context {// return context.WithValue(ctx, userKey, u)// }//// // FromContext returns the User value stored in ctx, if any.// func FromContext(ctx context.Context) (*User, bool) {// u, ok := ctx.Value(userKey).(*User)// return u, ok// }Value(key any) any}// Canceled is the error returned by [Context.Err] when the context is canceled// for some reason other than its deadline passing.var Canceled = errors.New("context canceled")// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled// due to its deadline passing.var DeadlineExceeded error = deadlineExceededError{}type deadlineExceededError struct{}func (deadlineExceededError) () string { return "context deadline exceeded" }func (deadlineExceededError) () bool { return true }func (deadlineExceededError) () bool { return true }// An emptyCtx is never canceled, has no values, and has no deadline.// It is the common base of backgroundCtx and todoCtx.type emptyCtx struct{}func (emptyCtx) () ( time.Time, bool) {return}func (emptyCtx) () <-chan struct{} {return nil}func (emptyCtx) () error {return nil}func (emptyCtx) ( any) any {return nil}type backgroundCtx struct{ emptyCtx }func (backgroundCtx) () string {return "context.Background"}type todoCtx struct{ emptyCtx }func (todoCtx) () string {return "context.TODO"}// Background returns a non-nil, empty [Context]. It is never canceled, has no// values, and has no deadline. It is typically used by the main function,// initialization, and tests, and as the top-level Context for incoming// requests.func () Context {return backgroundCtx{}}// TODO returns a non-nil, empty [Context]. Code should use context.TODO when// it's unclear which Context to use or it is not yet available (because the// surrounding function has not yet been extended to accept a Context// parameter).func () Context {return todoCtx{}}// A CancelFunc tells an operation to abandon its work.// A CancelFunc does not wait for the work to stop.// A CancelFunc may be called by multiple goroutines simultaneously.// After the first call, subsequent calls to a CancelFunc do nothing.type CancelFunc func()// WithCancel returns a derived context that points to the parent context// but has a new Done channel. The returned context's Done channel is closed// when the returned cancel function is called or when the parent context's// Done channel is closed, whichever happens first.//// Canceling this context releases resources associated with it, so code should// call cancel as soon as the operations running in this [Context] complete.func ( Context) ( Context, CancelFunc) {:= withCancel()return , func() { .cancel(true, Canceled, nil) }}// A CancelCauseFunc behaves like a [CancelFunc] but additionally sets the cancellation cause.// This cause can be retrieved by calling [Cause] on the canceled Context or on// any of its derived Contexts.//// If the context has already been canceled, CancelCauseFunc does not set the cause.// For example, if childContext is derived from parentContext:// - if parentContext is canceled with cause1 before childContext is canceled with cause2,// then Cause(parentContext) == Cause(childContext) == cause1// - if childContext is canceled with cause2 before parentContext is canceled with cause1,// then Cause(parentContext) == cause1 and Cause(childContext) == cause2type CancelCauseFunc func(cause error)// WithCancelCause behaves like [WithCancel] but returns a [CancelCauseFunc] instead of a [CancelFunc].// Calling cancel with a non-nil error (the "cause") records that error in ctx;// it can then be retrieved using Cause(ctx).// Calling cancel with nil sets the cause to Canceled.//// Example use://// ctx, cancel := context.WithCancelCause(parent)// cancel(myError)// ctx.Err() // returns context.Canceled// context.Cause(ctx) // returns myErrorfunc ( Context) ( Context, CancelCauseFunc) {:= withCancel()return , func( error) { .cancel(true, Canceled, ) }}func withCancel( Context) *cancelCtx {if == nil {panic("cannot create context from nil parent")}:= &cancelCtx{}.propagateCancel(, )return}// Cause returns a non-nil error explaining why c was canceled.// The first cancellation of c or one of its parents sets the cause.// If that cancellation happened via a call to CancelCauseFunc(err),// then [Cause] returns err.// Otherwise Cause(c) returns the same value as c.Err().// Cause returns nil if c has not been canceled yet.func ( Context) error {if , := .Value(&cancelCtxKey).(*cancelCtx); {.mu.Lock():= .cause.mu.Unlock()if != nil {return}// Either this context is not canceled,// or it is canceled and the cancellation happened in a// custom context implementation rather than a *cancelCtx.}// There is no cancelCtxKey value with a cause, so we know that c is// not a descendant of some canceled Context created by WithCancelCause.// Therefore, there is no specific cause to return.// If this is not one of the standard Context types,// it might still have an error even though it won't have a cause.return .Err()}// AfterFunc arranges to call f in its own goroutine after ctx is canceled.// If ctx is already canceled, AfterFunc calls f immediately in its own goroutine.//// Multiple calls to AfterFunc on a context operate independently;// one does not replace another.//// Calling the returned stop function stops the association of ctx with f.// It returns true if the call stopped f from being run.// If stop returns false,// either the context is canceled and f has been started in its own goroutine;// or f was already stopped.// The stop function does not wait for f to complete before returning.// If the caller needs to know whether f is completed,// it must coordinate with f explicitly.//// If ctx has a "AfterFunc(func()) func() bool" method,// AfterFunc will use it to schedule the call.func ( Context, func()) ( func() bool) {:= &afterFuncCtx{f: ,}.cancelCtx.propagateCancel(, )return func() bool {:= false.once.Do(func() {= true})if {.cancel(true, Canceled, nil)}return}}type afterFuncer interface {AfterFunc(func()) func() bool}type afterFuncCtx struct {cancelCtxonce sync.Once // either starts running f or stops f from runningf func()}func ( *afterFuncCtx) ( bool, , error) {.cancelCtx.cancel(false, , )if {removeChild(.Context, )}.once.Do(func() {go .f()})}// A stopCtx is used as the parent context of a cancelCtx when// an AfterFunc has been registered with the parent.// It holds the stop function used to unregister the AfterFunc.type stopCtx struct {Contextstop func() bool}// goroutines counts the number of goroutines ever created; for testing.var goroutines atomic.Int32// &cancelCtxKey is the key that a cancelCtx returns itself for.var cancelCtxKey int// parentCancelCtx returns the underlying *cancelCtx for parent.// It does this by looking up parent.Value(&cancelCtxKey) to find// the innermost enclosing *cancelCtx and then checking whether// parent.Done() matches that *cancelCtx. (If not, the *cancelCtx// has been wrapped in a custom implementation providing a// different done channel, in which case we should not bypass it.)func parentCancelCtx( Context) (*cancelCtx, bool) {:= .Done()if == closedchan || == nil {return nil, false}, := .Value(&cancelCtxKey).(*cancelCtx)if ! {return nil, false}, := .done.Load().(chan struct{})if != {return nil, false}return , true}// removeChild removes a context from its parent.func removeChild( Context, canceler) {if , := .(stopCtx); {.stop()return}, := parentCancelCtx()if ! {return}.mu.Lock()if .children != nil {delete(.children, )}.mu.Unlock()}// A canceler is a context type that can be canceled directly. The// implementations are *cancelCtx and *timerCtx.type canceler interface {cancel(removeFromParent bool, err, cause error)Done() <-chan struct{}}// closedchan is a reusable closed channel.var closedchan = make(chan struct{})func init() {close(closedchan)}// A cancelCtx can be canceled. When canceled, it also cancels any children// that implement canceler.type cancelCtx struct {Contextmu sync.Mutex // protects following fieldsdone atomic.Value // of chan struct{}, created lazily, closed by first cancel callchildren map[canceler]struct{} // set to nil by the first cancel callerr atomic.Value // set to non-nil by the first cancel callcause error // set to non-nil by the first cancel call}func ( *cancelCtx) ( any) any {if == &cancelCtxKey {return}return value(.Context, )}func ( *cancelCtx) () <-chan struct{} {:= .done.Load()if != nil {return .(chan struct{})}.mu.Lock()defer .mu.Unlock()= .done.Load()if == nil {= make(chan struct{}).done.Store()}return .(chan struct{})}func ( *cancelCtx) () error {// An atomic load is ~5x faster than a mutex, which can matter in tight loops.if := .err.Load(); != nil {return .(error)}return nil}// propagateCancel arranges for child to be canceled when parent is.// It sets the parent context of cancelCtx.func ( *cancelCtx) ( Context, canceler) {.Context =:= .Done()if == nil {return // parent is never canceled}select {case <-:// parent is already canceled.cancel(false, .Err(), Cause())returndefault:}if , := parentCancelCtx(); {// parent is a *cancelCtx, or derives from one..mu.Lock()if := .err.Load(); != nil {// parent has already been canceled.cancel(false, .(error), .cause)} else {if .children == nil {.children = make(map[canceler]struct{})}.children[] = struct{}{}}.mu.Unlock()return}if , := .(afterFuncer); {// parent implements an AfterFunc method..mu.Lock():= .AfterFunc(func() {.cancel(false, .Err(), Cause())}).Context = stopCtx{Context: ,stop: ,}.mu.Unlock()return}goroutines.Add(1)go func() {select {case <-.Done():.cancel(false, .Err(), Cause())case <-.Done():}}()}type stringer interface {String() string}func contextName( Context) string {if , := .(stringer); {return .String()}return reflectlite.TypeOf().String()}func ( *cancelCtx) () string {return contextName(.Context) + ".WithCancel"}// cancel closes c.done, cancels each of c's children, and, if// removeFromParent is true, removes c from its parent's children.// cancel sets c.cause to cause if this is the first time c is canceled.func ( *cancelCtx) ( bool, , error) {if == nil {panic("context: internal error: missing cancel error")}if == nil {=}.mu.Lock()if .err.Load() != nil {.mu.Unlock()return // already canceled}.err.Store().cause =, := .done.Load().(chan struct{})if == nil {.done.Store(closedchan)} else {close()}for := range .children {// NOTE: acquiring the child's lock while holding parent's lock..cancel(false, , )}.children = nil.mu.Unlock()if {removeChild(.Context, )}}// WithoutCancel returns a derived context that points to the parent context// and is not canceled when parent is canceled.// The returned context returns no Deadline or Err, and its Done channel is nil.// Calling [Cause] on the returned context returns nil.func ( Context) Context {if == nil {panic("cannot create context from nil parent")}return withoutCancelCtx{}}type withoutCancelCtx struct {c Context}func (withoutCancelCtx) () ( time.Time, bool) {return}func (withoutCancelCtx) () <-chan struct{} {return nil}func (withoutCancelCtx) () error {return nil}func ( withoutCancelCtx) ( any) any {return value(, )}func ( withoutCancelCtx) () string {return contextName(.c) + ".WithoutCancel"}// WithDeadline returns a derived context that points to the parent context// but has the deadline adjusted to be no later than d. If the parent's// deadline is already earlier than d, WithDeadline(parent, d) is semantically// equivalent to parent. The returned [Context.Done] channel is closed when// the deadline expires, when the returned cancel function is called,// or when the parent context's Done channel is closed, whichever happens first.//// Canceling this context releases resources associated with it, so code should// call cancel as soon as the operations running in this [Context] complete.func ( Context, time.Time) (Context, CancelFunc) {return WithDeadlineCause(, , nil)}// WithDeadlineCause behaves like [WithDeadline] but also sets the cause of the// returned Context when the deadline is exceeded. The returned [CancelFunc] does// not set the cause.func ( Context, time.Time, error) (Context, CancelFunc) {if == nil {panic("cannot create context from nil parent")}if , := .Deadline(); && .Before() {// The current deadline is already sooner than the new one.return WithCancel()}:= &timerCtx{deadline: ,}.cancelCtx.propagateCancel(, ):= time.Until()if <= 0 {.cancel(true, DeadlineExceeded, ) // deadline has already passedreturn , func() { .cancel(false, Canceled, nil) }}.mu.Lock()defer .mu.Unlock()if .err.Load() == nil {.timer = time.AfterFunc(, func() {.cancel(true, DeadlineExceeded, )})}return , func() { .cancel(true, Canceled, nil) }}// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to// implement Done and Err. It implements cancel by stopping its timer then// delegating to cancelCtx.cancel.type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time}func ( *timerCtx) () ( time.Time, bool) {return .deadline, true}func ( *timerCtx) () string {return contextName(.cancelCtx.Context) + ".WithDeadline(" +.deadline.String() + " [" +time.Until(.deadline).String() + "])"}func ( *timerCtx) ( bool, , error) {.cancelCtx.cancel(false, , )if {// Remove this timerCtx from its parent cancelCtx's children.removeChild(.cancelCtx.Context, )}.mu.Lock()if .timer != nil {.timer.Stop().timer = nil}.mu.Unlock()}// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).//// Canceling this context releases resources associated with it, so code should// call cancel as soon as the operations running in this [Context] complete://// func slowOperationWithTimeout(ctx context.Context) (Result, error) {// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)// defer cancel() // releases resources if slowOperation completes before timeout elapses// return slowOperation(ctx)// }func ( Context, time.Duration) (Context, CancelFunc) {return WithDeadline(, time.Now().Add())}// WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the// returned Context when the timeout expires. The returned [CancelFunc] does// not set the cause.func ( Context, time.Duration, error) (Context, CancelFunc) {return WithDeadlineCause(, time.Now().Add(), )}// WithValue returns a derived context that points to the parent Context.// In the derived context, the value associated with key is val.//// Use context Values only for request-scoped data that transits processes and// APIs, not for passing optional parameters to functions.//// The provided key must be comparable and should not be of type// string or any other built-in type to avoid collisions between// packages using context. Users of WithValue should define their own// types for keys. To avoid allocating when assigning to an// interface{}, context keys often have concrete type// struct{}. Alternatively, exported context key variables' static// type should be a pointer or interface.func ( Context, , any) Context {if == nil {panic("cannot create context from nil parent")}if == nil {panic("nil key")}if !reflectlite.TypeOf().Comparable() {panic("key is not comparable")}return &valueCtx{, , }}// A valueCtx carries a key-value pair. It implements Value for that key and// delegates all other calls to the embedded Context.type valueCtx struct {Contextkey, val any}// stringify tries a bit to stringify v, without using fmt, since we don't// want context depending on the unicode tables. This is only used by// *valueCtx.String().func stringify( any) string {switch s := .(type) {case stringer:return .String()case string:returncase nil:return "<nil>"}return reflectlite.TypeOf().String()}func ( *valueCtx) () string {return contextName(.Context) + ".WithValue(" +stringify(.key) + ", " +stringify(.val) + ")"}func ( *valueCtx) ( any) any {if .key == {return .val}return value(.Context, )}func value( Context, any) any {for {switch ctx := .(type) {case *valueCtx:if == .key {return .val}= .Contextcase *cancelCtx:if == &cancelCtxKey {return}= .Contextcase withoutCancelCtx:if == &cancelCtxKey {// This implements Cause(ctx) == nil// when ctx is created using WithoutCancel.return nil}= .ccase *timerCtx:if == &cancelCtxKey {return &.cancelCtx}= .Contextcase backgroundCtx, todoCtx:return nildefault:return .Value()}}}
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |