Chapter 11

contextの内部実体

さき(H.Saki)
さき(H.Saki)
2021.08.28に更新

この章について

ここからは、ここまであえて言及してこなかった「contextインターフェース」について触れていきたいと思います。

context「インターフェース」?

context.Context型の定義をよくよく見てみると、実はインターフェースじゃないか、というところに気付いていただけるかと思います。

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

出典:pkg.go.dev - context.Context

インターフェースということは、これを満たす具体型があるはずです。
ここからはその「contextになりうる具体型」を探しにいきましょう。

具体型一覧

contextパッケージの中には、context.Contextインターフェースを満たす具体型が4つ存在します。

context.emptyCtx型

まず一つが、context.emptyCtx型です。

// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int

出典:context/context.go

これはcontext.Backgroundcontext.TODOを呼んだときにできる空インターフェースを表現するために作られたものです。
キャンセルすることもできず、値やデッドラインを持ちません。

context.cancelCtx型

context.cancelCtx型は、内部にdoneチャネルをもち、キャンセル伝播を行うことができるcontextを表します。
また、errフィールドの中には、contextのErrメソッドで取得できるキャンセル理由のエラーが格納されます。

// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     atomic.Value          // of chan struct{}, created lazily, closed by first cancel call
	children map[canceler]struct{} // set to nil by the first cancel call
	err      error                 // set to non-nil by the first cancel call
}

出典:context/context.go

context.timerCtx型

context.timerCtxは内部にcancelCtxを持った上で、タイムアウトのカウントをするためのタイマーも持ち合わせています。

// 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 {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

出典:context/context.go

context.valueCtx型

context.valueCtxは、内部にkey-valueセットを持っています。
key,valフィールドにセットされた内容+valueCtx内部に持っているContextが持っているkey-valueのセットが、Valueメソッドで取ってこれる内容です。

// 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 {
	Context
	key, val interface{}
}

出典:context/context.go

具体型をまとめるインターフェースのメリット

このように、contextの機能である

  • キャンセル伝播
  • タイムアウト実装
  • 値の伝達

は、実は全部違う型で実装されているのです。

これらの違う型をすべて「インターフェース」としてまとめて扱うために、contextはインターフェースとして公開されているのです。

// インターフェースがなかったら
func MyFuncWithCancel(ctx context.CancelCtx) // キャンセル機能があるcontextを受け取る場合
func MyFuncWithTimeout(ctx context.TimerCtx) // タイムアウト機能があるcontextを受け取る場合
func MyFuncWithValue(ctx context.ValueCtx) // 値伝達機能があるcontextを受け取る場合// インターフェースがあると
func MyFunc(ctx context.Context) // これで済む