Context is used to control the life cycle of a request, as well as one of the data sharing in the cycle package
.
data plane
Dependency valueCtx
, is map
a
type valueCtx struct {
Context
key, val interface{}
}
Read the latest updated value through recursive matching (considering that there should be not much data in a request cycle, recursive query should be acceptable)
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}
Data is written by nesting.
func WithValue(parent Context, key, val interface{}) Context {
...
return &valueCtx{parent, key, val}
}
The life cycle
Context
The management of the life cycle, or the implementation of the notification mechanism on exit, is mainly dependent on cancelCtx
.
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done 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
}
When it is cancelCtx
first Context
derived from , the background goroutine
will synchronize 退出
events by means of . select
Detect the exit by parent
, then exit
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done(): // block 机制,避免立即退出
}
}()
If it is derived from the derived object again, the derivation relationship is maintained through the children
field . When parent
Exit, to actively exit all derivationsContext
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
cancel
the main logic of
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
When channel
is closed, all other select
listening routine
will perceive, thus realizing the notification mechanism.
WithDeadline
On this basis, a timer
mechanism to realize its own active exit through the background.
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
c.timer = time.AfterFunc(d, func() {
c.cancel(true, DeadlineExceeded)
})
The main logic is almost the same