Skip to content

Commit 6e522d1

Browse files
committed
update sem
1 parent 6ae7a25 commit 6e522d1

File tree

1 file changed

+47
-53
lines changed

1 file changed

+47
-53
lines changed

semaphore.md

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ var semtable [semTabSize]struct {
3838
root semaRoot
3939
pad [sys.CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
4040
}
41+
42+
func semroot(addr *uint32) *semaRoot {
43+
return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
44+
}
4145
```
4246

4347
## 对外封装
@@ -75,16 +79,25 @@ func poll_runtime_Semrelease(addr *uint32) {
7579

7680
sem 本身支持 acquire 和 release,其实就是 OS 里常说的 P 操作和 V 操作。
7781

78-
### acquire 过程
82+
### 公共部分
7983

8084
```go
81-
func readyWithTime(s *sudog, traceskip int) {
82-
if s.releasetime != 0 {
83-
s.releasetime = cputicks()
85+
func cansemacquire(addr *uint32) bool {
86+
for {
87+
v := atomic.Load(addr)
88+
if v == 0 {
89+
return false
90+
}
91+
if atomic.Cas(addr, v, v-1) {
92+
return true
93+
}
8494
}
85-
goready(s.g, traceskip)
8695
}
96+
```
97+
98+
### acquire 过程
8799

100+
```go
88101
type semaProfileFlags int
89102

90103
const (
@@ -103,45 +116,37 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags) {
103116
throw("semacquire not on the G stack")
104117
}
105118

106-
// Easy case.
119+
// 低成本的情况
107120
if cansemacquire(addr) {
108121
return
109122
}
110123

111-
// Harder case:
112-
// increment waiter count
113-
// try cansemacquire one more time, return if succeeded
114-
// enqueue itself as a waiter
124+
// 高成本的情况:
125+
// 增加 waiter count 的值
126+
// 再尝试调用一次 cansemacquire,成本了就直接返回
127+
// 没成功就把自己作为一个 waiter 入队
115128
// sleep
116-
// (waiter descriptor is dequeued by signaler)
129+
// (之后 waiter descriptor 被 signaler 用 dequeue 踢出)
117130
s := acquireSudog()
118131
root := semroot(addr)
119132
t0 := int64(0)
120133
s.releasetime = 0
121134
s.acquiretime = 0
122135
s.ticket = 0
123-
if profile&semaBlockProfile != 0 && blockprofilerate > 0 {
124-
t0 = cputicks()
125-
s.releasetime = -1
126-
}
127-
if profile&semaMutexProfile != 0 && mutexprofilerate > 0 {
128-
if t0 == 0 {
129-
t0 = cputicks()
130-
}
131-
s.acquiretime = t0
132-
}
136+
133137
for {
134138
lock(&root.lock)
135-
// Add ourselves to nwait to disable "easy case" in semrelease.
139+
// nwait 加一,这样后来的就不会在 semrelease 中进低成本的路径了
136140
atomic.Xadd(&root.nwait, 1)
137-
// Check cansemacquire to avoid missed wakeup.
141+
// 检查 cansemacquire 避免错过了唤醒
138142
if cansemacquire(addr) {
139143
atomic.Xadd(&root.nwait, -1)
140144
unlock(&root.lock)
141145
break
142146
}
143-
// Any semrelease after the cansemacquire knows we're waiting
144-
// (we set nwait above), so go to sleep.
147+
// 在 cansemacquire 之后的 semrelease 都可以知道我们正在等待
148+
// (上面设置了 nwait),所以会直接进入 sleep
149+
// 注: 这里说的 sleep 其实就是 goparkunlock
145150
root.queue(addr, s, lifo)
146151
goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4)
147152
if s.ticket != 0 || cansemacquire(addr) {
@@ -166,18 +171,18 @@ func semrelease1(addr *uint32, handoff bool) {
166171
root := semroot(addr)
167172
atomic.Xadd(addr, 1)
168173

169-
// Easy case: no waiters?
170-
// This check must happen after the xadd, to avoid a missed wakeup
171-
// (see loop in semacquire).
174+
// 低成本情况: 没有 waiter?
175+
// 这个 atomic 的检查必须发生在 xadd 之前,以避免错误唤醒
176+
// (具体参见 semacquire 中的循环)
172177
if atomic.Load(&root.nwait) == 0 {
173178
return
174179
}
175180

176-
// Harder case: search for a waiter and wake it.
181+
// 高成本情况: 搜索 waiter 并唤醒它
177182
lock(&root.lock)
178183
if atomic.Load(&root.nwait) == 0 {
179-
// The count is already consumed by another goroutine,
180-
// so no need to wake up another goroutine.
184+
// count 值已经被另一个 goroutine 消费了
185+
// 所以我们不需要唤醒其它 goroutine
181186
unlock(&root.lock)
182187
return
183188
}
@@ -186,7 +191,7 @@ func semrelease1(addr *uint32, handoff bool) {
186191
atomic.Xadd(&root.nwait, -1)
187192
}
188193
unlock(&root.lock)
189-
if s != nil { // May be slow, so unlock first
194+
if s != nil { // 可能会很慢,所以先解锁
190195
acquiretime := s.acquiretime
191196
if acquiretime != 0 {
192197
mutexevent(t0-acquiretime, 3)
@@ -201,27 +206,19 @@ func semrelease1(addr *uint32, handoff bool) {
201206
}
202207
}
203208

204-
func semroot(addr *uint32) *semaRoot {
205-
return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
206-
}
207-
208-
func cansemacquire(addr *uint32) bool {
209-
for {
210-
v := atomic.Load(addr)
211-
if v == 0 {
212-
return false
213-
}
214-
if atomic.Cas(addr, v, v-1) {
215-
return true
216-
}
209+
func readyWithTime(s *sudog, traceskip int) {
210+
if s.releasetime != 0 {
211+
s.releasetime = cputicks()
217212
}
213+
goready(s.g, traceskip)
218214
}
219215
```
220216

221217
### treap 结构
222218

223219
```go
224-
// queue adds s to the blocked goroutines in semaRoot.
220+
// queue 函数会把 s 添加到 semaRoot 上阻塞的 goroutine 们中
221+
// 实际上就是把 s 添加到其地址对应的 treap 上
225222
func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
226223
s.g = getg()
227224
s.elem = unsafe.Pointer(addr)
@@ -234,7 +231,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
234231
if t.elem == unsafe.Pointer(addr) {
235232
// Already have addr in list.
236233
if lifo {
237-
// Substitute s in t's place in treap.
234+
// treap 中在 t 的位置用 s 覆盖掉 t
238235
*pt = s
239236
s.ticket = t.ticket
240237
s.acquiretime = t.acquiretime
@@ -247,7 +244,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
247244
if s.next != nil {
248245
s.next.parent = s
249246
}
250-
// Add t first in s's wait list.
247+
// t 放在 s 的 wait list 的第一个位置
251248
s.waitlink = t
252249
s.waittail = t.waittail
253250
if s.waittail == nil {
@@ -258,7 +255,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
258255
t.next = nil
259256
t.waittail = nil
260257
} else {
261-
// Add s to end of t's wait list.
258+
// s 添加到 t 的等待列表的末尾
262259
if t.waittail == nil {
263260
t.waitlink = s
264261
} else {
@@ -514,10 +511,7 @@ func notifyListWait(l *notifyList, t uint32) {
514511
s.ticket = t
515512
s.releasetime = 0
516513
t0 := int64(0)
517-
if blockprofilerate > 0 {
518-
t0 = cputicks()
519-
s.releasetime = -1
520-
}
514+
521515
if l.tail == nil {
522516
l.head = s
523517
} else {

0 commit comments

Comments
 (0)