context

package
v1.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2024 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package context は、期限、キャンセルシグナル、および他のAPI境界やプロセス間を超えたリクエストスコープの値を伝達するContext型を定義します。

サーバーへの入力リクエストは Context を作成し、サーバーへの出力呼び出しは[Context]を受け入れる必要があります。 それらの間の関数呼び出しのチェーンは、Contextを伝播させ、WithCancelWithDeadlineWithTimeout、または WithValue を使用して作成された派生Contextで置き換えることができます。 Contextがキャンセルされると、それから派生したすべてのContextもキャンセルされます。

WithCancelWithDeadline、および WithTimeout 関数は、Context(親)を取得し、派生Context(子)と CancelFunc を返します。 CancelFuncを呼び出すと、子とその子がキャンセルされ、親の子への参照が削除され、関連するタイマーが停止します。 CancelFuncを呼び出さないと、子とその子は親がキャンセルされるか、タイマーが発火するまでリークします。 go vetツールは、CancelFuncがすべての制御フローパスで使用されていることを確認します。

WithCancelCause 関数は CancelCauseFunc を返し、エラーを受け取り、キャンセルの原因として記録します。 キャンセルされたコンテキストまたはその子のいずれかで Cause を呼び出すと、原因が取得されます。 原因が指定されていない場合、 Cause(ctx) は ctx.Err() と同じ値を返します。

Contextを使用するプログラムは、これらのルールに従う必要があります。 これにより、パッケージ間でインターフェースを一貫させ、静的解析ツールがコンテキストの伝播をチェックできるようになります。

構造体型の内部にContextを格納しないでください。 代わりに、それが必要な各関数に明示的にContextを渡してください。 通常、最初のパラメーターにctxという名前を付けます。

func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

関数がnilの Context を許可していても、nilの Context を渡さないでください。 どの Context を使用するかわからない場合は、 context.TODO を渡してください。

コンテキストの値は、オプションのパラメータを関数に渡すためではなく、プロセスやAPIを超えるリクエストスコープのデータにのみ使用してください。

同じContextは、異なるゴルーチンで実行される関数に渡すことができます。Contextは、複数のゴルーチンによる同時使用に対して安全です。

サーバーでContextを使用する例のコードについては、https://blog.golang.org/contextを参照してください。

Index

Examples

Constants

This section is empty.

Variables

View Source
var Canceled = errors.New("context canceled")

Canceled コンテキストがキャンセルされた場合に [Context.Err] が返すエラーです。

View Source
var DeadlineExceeded error = deadlineExceededError{}

DeadlineExceeded コンテキストの期限が切れた場合に [Context.Err] が返すエラーです。

Functions

func AfterFunc added in v1.21.0

func AfterFunc(ctx Context, f func()) (stop func() bool)

AfterFunc ctxが完了(キャンセルまたはタイムアウト)した後、fを独自のゴルーチンで呼び出すように設定します。 もしctxが既に完了している場合、AfterFuncは独自のゴルーチンで直ちにfを呼び出します。

ContextでのAfterFuncの複数回の呼び出しは独立して動作し、1つが他を置き換えることはありません。

返されたstop関数を呼び出すと、ctxとfの関連付けが停止します。 呼び出しがfの実行を停止した場合、trueを返します。 stopがfalseを返す場合、 コンテキストが完了し、fが独自のゴルーチンで開始されたか、 またはfが既に停止されています。 stop関数は、fが完了するのを待ってから戻りません。 呼び出し元がfが完了したかどうかを知る必要がある場合、 明示的にfと調整する必要があります。

ctxに「AfterFunc(func()) func() bool」メソッドがある場合、 AfterFuncはそれを使用して呼び出しをスケジュールします。

Example (Cond)

This example uses AfterFunc to define a function which waits on a sync.Cond, stopping the wait when a context is canceled.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
	"github.com/shogo82148/std/sync"
	"github.com/shogo82148/std/time"
)

func main() {
	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
Example (Connection)

This example uses AfterFunc to define a function which reads from a net.Conn, stopping the read when a context is canceled.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
	"github.com/shogo82148/std/net"
	"github.com/shogo82148/std/time"
)

func main() {
	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() {
			// AfterFuncが開始されました。
			// 完了するまで待ち、Connの期限をリセットします。
			<-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
Example (Merge)

This example uses AfterFunc to define a function which combines the cancellation signals of two Contexts.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/errors"
	"github.com/shogo82148/std/fmt"
)

func main() {
	// mergeCancelは、ctxの値を含み、ctxまたはcancelCtxのいずれかがキャンセルされたときにキャンセルされるコンテキストを返します。
	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

func Cause added in v1.20.0

func Cause(c Context) error

Causeは、cがキャンセルされた理由を説明する非nilのエラーを返します。 cまたはその親の最初のキャンセルは原因を設定します。 そのキャンセルがCancelCauseFunc(err)の呼び出しによって行われた場合、 Cause はerrを返します。 そうでない場合、Cause(c)はc.Err()と同じ値を返します。 cがまだキャンセルされていない場合、Causeはnilを返します。

func WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel 新しいDoneチャネルを持つ親のコピーを返します。 返されたコンテキストのDoneチャネルは、返されたキャンセル関数が呼び出されるか、 または親のコンテキストのDoneチャネルが閉じられたとき、より早く閉じられます。

このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、コードはこのContextで実行される操作が完了したらすぐにcancelを呼び出す必要があります。

Example

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.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
)

func main() {
	// genは、別のゴルーチンで整数を生成し、それらを返されたチャネルに送信します。
	// genの呼び出し元は、生成された整数を消費し終わったらコンテキストをキャンセルする必要があります。
	// そうしないと、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() // 整数を消費し終わったらキャンセル

	for n := range gen(ctx) {
		fmt.Println(n)
		if n == 5 {
			break
		}
	}
}
Output:

1
2
3
4
5

func WithCancelCause added in v1.20.0

func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)

WithCancelCause WithCancel と同様に動作しますが、 CancelFunc の代わりに CancelCauseFunc を返します。 エラー(「原因」と呼ばれる)を非nilで渡すと、そのエラーがctxに記録されます。 その後、Cause を使用して取得できます。 nilでキャンセルすると、原因は Canceled に設定されます。

使用例:

ctx、cancel := context.WithCancelCause(parent)
cancel(myError)
ctx.Err() // context.Canceledを返します
context.Cause(ctx) // myErrorを返します

func WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadlineは、親の期限をdよりも遅くならないように調整した親のコピーを返します。 親の期限がすでにdよりも早い場合、 WithDeadline(parent, d) は親と意味的に等価です。 返された [Context.Done] チャネルは、期限が切れたとき、返されたキャンセル関数が呼び出されたとき、または親のコンテキストのDoneチャネルが閉じられたときのいずれかが最初に発生したときに閉じられます。

このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、コードはこの Context で実行される操作が完了したらすぐにcancelを呼び出す必要があります。

Example

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.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
	"github.com/shogo82148/std/time"
)

var shortDuration time.Duration = 1
var neverReady <-chan time.Time

func main() {
	d := time.Now().Add(shortDuration)
	ctx, cancel := context.WithDeadline(context.Background(), d)

	// ctxが期限切れになっている場合でも、そのキャンセル関数を呼び出すことは良い習慣です。
	// そうしないと、コンテキストとその親が必要以上に長く生き残る可能性があります。
	defer cancel()

	select {
	case <-neverReady:
		fmt.Println("ready")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}

}
Output:

context deadline exceeded

func WithDeadlineCause added in v1.21.0

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)

WithDeadlineCause WithDeadline と同様に動作しますが、期限が切れたときに返された Context の原因も設定します。 返された CancelFunc は原因を設定しません。

func WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).

このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、 コードはこの Context で実行される操作が完了したらすぐにcancelを呼び出す必要があります。

func slowOperationWithTimeout(ctx context.Context) (Result, error) {
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()  // slowOperationがタイムアウトが経過する前に完了した場合、リソースが解放されます
	return slowOperation(ctx)
}
Example

This example passes a context with a timeout to tell a blocking function that it should abandon its work after the timeout elapses.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
	"github.com/shogo82148/std/time"
)

var shortDuration time.Duration = 1
var neverReady <-chan time.Time

func main() {
	// タイムアウト付きのコンテキストを渡すことで、ブロッキング関数に、タイムアウトが経過した後に作業を中止するように指示します。
	ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
	defer cancel()

	select {
	case <-neverReady:
		fmt.Println("ready")
	case <-ctx.Done():
		fmt.Println(ctx.Err()) // "context deadline exceeded" を出力します
	}

}
Output:

context deadline exceeded

func WithTimeoutCause added in v1.21.0

func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)

WithTimeoutCause [WithTimeout]と同様に動作しますが、タイムアウトが切れたときに返された Context の原因も設定します。 返された CancelFunc は原因を設定しません。

Types

type CancelCauseFunc added in v1.20.0

type CancelCauseFunc func(cause error)

CancelCauseFunc [CancelFunc]と同様に動作しますが、キャンセルの原因を設定します。 この原因は、キャンセルされたContextまたはその派生Contextのいずれかで Cause を呼び出すことで取得できます。

コンテキストが既にキャンセルされている場合、 CancelCauseFunc は原因を設定しません。 たとえば、childContextがparentContextから派生している場合:

  • childContextがcause2でキャンセルされる前に、parentContextがcause1でキャンセルされた場合、 その後、Cause(parentContext) == Cause(childContext) == cause1
  • parentContextがcause1でキャンセルされる前に、childContextがcause2でキャンセルされた場合、 その後、Cause(parentContext) == cause1 および Cause(childContext) == cause2

type CancelFunc

type CancelFunc func()

CancelFunc 操作がその作業を中止するように指示します。 CancelFuncは、作業が停止するのを待ちません。 CancelFuncは、複数のゴルーチンから同時に呼び出すことができます。 最初の呼び出しの後、CancelFuncへの後続の呼び出しは何もしません。

type Context

type Context interface {
	Deadline() (deadline time.Time, ok bool)

	Done() <-chan struct{}

	Err() error

	Value(key any) any
}

Context は、期限、キャンセルシグナル、および他の値をAPI境界を超えて伝達します。

Contextのメソッドは、複数のゴルーチンから同時に呼び出すことができます。

func Background

func Background() Context

Backgroundは、非nilで空の[Context]を返します。キャンセルされることはなく、値も期限もありません。 通常、main関数、初期化、テスト、および着信リクエストのトップレベルContextとして使用されます。

func TODO

func TODO() Context

TODO 非nilで空の Context を返します。 コードがどの[Context]を使用するか不明である場合や、まだ Context パラメータを受け入れるように拡張されていない (周囲の関数がまだ[Context]を受け入れるように拡張されていない)場合に、コードは context.TODO を使用する必要があります。

func WithValue

func WithValue(parent Context, key, val any) Context

WithValueは、キーに関連付けられた値がvalである親のコピーを返します。

コンテキストの値は、プロセスやAPIを超えて転送されるリクエストスコープのデータにのみ使用し、関数にオプションのパラメータを渡すために使用しないでください。

提供されたキーは比較可能である必要があり、衝突を避けるためにstringまたは他の組み込み型であってはなりません。 WithValueを使用するユーザーは、キーのために独自の型を定義する必要があります。 interface{} に代入するときのアロケーションを避けるために、コンテキストキーは通常、具体的な型 struct{} を持ちます。 代替案として、エクスポートされたコンテキストキー変数の静的型はポインタまたはインターフェースである必要があります。

Example

This example demonstrates how a value can be passed to the context and also how to retrieve it if it exists.

package main

import (
	"github.com/shogo82148/std/context"
	"github.com/shogo82148/std/fmt"
)

func main() {
	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

func WithoutCancel added in v1.21.0

func WithoutCancel(parent Context) Context

WithoutCancel returns a copy of parent that 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL