Skip to content

Commit 7a02315

Browse files
committed
feat: golang runtime preempt
1 parent 2da06b3 commit 7a02315

5 files changed

+1097
-0
lines changed

Diff for: README.md

+8
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@
163163

164164
- [golang deadlock 死锁检测的实现原理](https://github.com/rfyiamcool/notes/blob/main/golang_deadlock_atuo_detect.md)
165165

166+
- [golang udp 高性能网络优化](https://github.com/rfyiamcool/notes/blob/main/golang_udp_performance.md)
167+
168+
- [源码分析 golang context 的超时及关闭实现](https://github.com/rfyiamcool/notes/blob/main/golang_context_timeout_close.md)
169+
170+
- [golang 编译期优化 defer 调用性能的实现原理](https://github.com/rfyiamcool/notes/blob/main/golang_defer_optimize.md)
171+
172+
- [golang 基于信号的抢占式调度的设计实现原理](https://github.com/rfyiamcool/notes/blob/main/golang_runtime_preempt.md)
173+
166174
- [golang在mac m1下的性能表现](https://github.com/rfyiamcool/notes/blob/main/mac_m1_golang_benchmark.md)
167175

168176
- [istio envoy 的性能测试](https://github.com/rfyiamcool/notes/blob/main/istio_sidecar_performance.md)

Diff for: golang_context_timeout_close.md

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# 源码分析 golang context 的超时及关闭实现
2+
3+
Golang的context的作用就不多说了,就是用来管理调用上下文的,控制一个请求的生命周期。golang的context库里有四个组件。 withCancel用来控制取消事件,withDeadline 和 withTimeout 是控制超时,withValue可以传递一些key value。
4+
5+
```go
6+
// xiaorui.cc
7+
8+
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
9+
10+
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
11+
12+
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
13+
14+
func WithValue(parent Context, key, val interface{}) Context
15+
```
16+
17+
## 源码分析context
18+
19+
下面的结构图就很形象的说明了各个 context 的关联关系。context 节点通过 children map 来连接子 context 节点。总之,context节点是层层关联的。关闭的时候自然也是层层关闭。
20+
21+
![https://xiaorui.cc/wp-content/uploads/2018/12/20181201015259_78633.jpg](https://xiaorui.cc/wp-content/uploads/2018/12/20181201015259_78633.jpg)
22+
23+
WithTimeout 也是通过 WithDeadline 来实现的。
24+
25+
```go
26+
// xiaorui.cc
27+
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
28+
return WithDeadline(parent, time.Now().Add(timeout))
29+
}
30+
```
31+
32+
### context WithDeadline 的实现
33+
34+
通过下面的 WithDeadline 方法,我们可以分析出创建一个子 context 及定时器过程。
35+
36+
```go
37+
// xiaorui.cc
38+
type timerCtx struct {
39+
cancelCtx
40+
timer *time.Timer // Under cancelCtx.mu.
41+
42+
deadline time.Time
43+
}
44+
45+
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
46+
...
47+
c := &timerCtx{
48+
cancelCtx: newCancelCtx(parent), // 返回一个子的context
49+
deadline: d,
50+
}
51+
// 添加父节点和子节点的关联关系
52+
propagateCancel(parent, c)
53+
dur := time.Until(d)
54+
if dur <= 0 {
55+
c.cancel(true, DeadlineExceeded) // deadline has already passed
56+
return c, func() { c.cancel(true, Canceled) }
57+
}
58+
c.mu.Lock()
59+
defer c.mu.Unlock()
60+
if c.err == nil {
61+
// 添加定时器
62+
c.timer = time.AfterFunc(dur, func() {
63+
c.cancel(true, DeadlineExceeded)
64+
})
65+
}
66+
// 返回 context 和 关闭的方法
67+
return c, func() { c.cancel(true, Canceled) }
68+
}
69+
70+
// newCancelCtx returns an initialized cancelCtx.
71+
func newCancelCtx(parent Context) cancelCtx {
72+
return cancelCtx{Context: parent}
73+
}
74+
75+
func propagateCancel(parent Context, child canceler) {
76+
// ...
77+
// ...
78+
79+
} else {
80+
go func() {
81+
select {
82+
case <-parent.Done():
83+
child.cancel(false, parent.Err())
84+
case <-child.Done():
85+
}
86+
}()
87+
}
88+
}
89+
```
90+
91+
timectx 的 cancel 实现.
92+
93+
```go
94+
// xiaorui.cc
95+
func (c *timerCtx) cancel(removeFromParent bool, err error) {
96+
...
97+
if removeFromParent {
98+
// Remove this timerCtx from its parent cancelCtx's children.
99+
removeChild(c.cancelCtx.Context, c)
100+
}
101+
c.mu.Lock()
102+
if c.timer != nil {
103+
// 关闭定时器
104+
c.timer.Stop()
105+
c.timer = nil
106+
}
107+
c.mu.Unlock()
108+
}
109+
110+
// removeChild removes a context from its parent.
111+
func removeChild(parent Context, child canceler) {
112+
p, ok := parentCancelCtx(parent)
113+
if !ok {
114+
return
115+
}
116+
p.mu.Lock()
117+
if p.children != nil {
118+
delete(p.children, child) // 从父层里删除子节点
119+
}
120+
p.mu.Unlock()
121+
}
122+
```
123+
124+
## context cancel 关闭的设计
125+
126+
```go
127+
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
128+
...
129+
c.mu.Lock()
130+
...
131+
if c.done == nil {
132+
c.done = closedchan
133+
} else {
134+
close(c.done) // 关闭
135+
}
136+
// 重点: 层层关闭,从父层到子层,一个个的关闭.
137+
for child := range c.children {
138+
child.cancel(false, err)
139+
}
140+
c.children = nil
141+
c.mu.Unlock()
142+
143+
// 从父context child里删除子context节点
144+
if removeFromParent {
145+
removeChild(c.Context, c)
146+
}
147+
}
148+
```
149+
150+
## 总结:
151+
152+
context的源码很简单,代码也精简的,有兴趣的朋友可以细细的琢磨下。

0 commit comments

Comments
 (0)