(chan struct{})
func init() {
close(closedchan)
}
// cancel cancelCtx 取消操作, 关闭 c.done, 取消每个 child context, removeFromParent is true 从 parent 删除 child
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
for child := range c.children {
// NOTE: 保留 parent 锁, 继续获取 child 锁
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
// cancel timerCtx 取消操作
func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// propagateCancel parent 取消 map 中添加 child 子项
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // parent is never canceled
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
// CancelFunc cancel func 行为
type CancelFunc func()
// WithCancel 基于 parent context 构造可取消的 child context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// WithDeadline 返回 child context 并调整 parent deadline
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// 当前截止日期早于新设置的截止日期, 直接使用当前截止日期
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: d,
}
propagateCancel(parent, c)
dur := time.Until(d)
if dur <= 0 {
c.cancel(true, DeadlineExceeded) // deadline has already passed
return c, func() { c.cancel(false, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded)
})
}
return c, func() { c.cancel(true, Canceled) }
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout))
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
看到 with.go 是不是有种 mmp 感觉, 第一问还好, 第二问也爽爽, 这第三问怎么就有了压轴最后一问思索感 ~
其实还好, 多关怀下 propagateCancel 和 cancelCtx.cancel 操作怎么打配合. 其实上面源码中最难写的是
reflect 和 time (errors 和 sync 接触多好理解) 有兴趣的同行可以深入研究 . 水文该说再见了, 希望大家有
所得 ~
后记 - 代码和注释并存
错误是难免的, 欢迎勘误 ~ 共同成长提高 ?
<窗外雨> - https://music.163.com/#/song?id=514543382
|