@@ -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
7680sem 本身支持 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
88101type semaProfileFlags int
89102
90103const (
@@ -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 上
225222func (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