// 信号量处理 等待被唤醒 funcsemacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes int, reason waitReason) { gp := getg() if gp != gp.m.curg { throw("semacquire not on the G stack") }
// 出队单个sudog // 如果正在分析 sudog 则 dequeue 将返回唤醒它的时间 // 否则返为0 // dequeue searches for and finds the first goroutine // in semaRoot blocked on addr. // If the sudog was being profiled, dequeue returns the time // at which it was woken up as now. Otherwise now is 0. func(root *semaRoot) dequeue(addr *uint32) (found *sudog, now int64) { ps := &root.treap s := *ps for ; s != nil; s = *ps { if s.elem == unsafe.Pointer(addr) { // 找到才执行出队操作 goto Found } ifuintptr(unsafe.Pointer(addr)) < uintptr(s.elem) { ps = &s.prev } else { ps = &s.next } } // 找不到直接返回 returnnil, 0
Found: now = int64(0) if s.acquiretime != 0 { now = cputicks() } if t := s.waitlink; t != nil { // 有链表元素 直接移除队首元素 不用删树节点 *ps = t t.ticket = s.ticket t.parent = s.parent t.prev = s.prev if t.prev != nil { t.prev.parent = t } t.next = s.next if t.next != nil { t.next.parent = t } if t.waitlink != nil { t.waittail = s.waittail } else { t.waittail = nil } // 重置获取时间 t.acquiretime = now s.waitlink = nil s.waittail = nil } else { // 没有后续链表元素 需要删除树节点 进行树的再平衡 // 向下旋转为叶子节点 然后删除 for s.next != nil || s.prev != nil { // 有非空子树 就进行旋转 将 s 旋转为叶子节点 if s.next == nil || s.prev != nil && s.prev.ticket < s.next.ticket { // 左右子树皆不为空时 需要判断 左右节点的 ticket 保证优先级高度 root.rotateRight(s) } else { // 否则 左旋 root.rotateLeft(s) } } // 左右子树皆为空 即叶子节点 可以安全删除 // Remove s, now a leaf. if s.parent != nil { // 有父节点 移除父节点指向 if s.parent.prev == s { s.parent.prev = nil } else { s.parent.next = nil } } else { // 没有父节点 说明是最后一个节点 root.treap = nil } } // 清理 s 无关数据 s.parent = nil s.elem = nil s.next = nil s.prev = nil // 出队清空 ticket s.ticket = 0 return s, now }
// rotateLeft rotates the tree rooted at node x. // turning (x a (y b c)) into (y (x a b) c). func(root *semaRoot) rotateLeft(x *sudog) { // p -> (x a (y b c)) p := x.parent y := x.next b := y.prev
y.prev = x x.parent = y x.next = b if b != nil { b.parent = x }
y.parent = p if p == nil { root.treap = y } elseif p.prev == x { p.prev = y } else { if p.next != x { throw("semaRoot rotateLeft") } p.next = y } }
/* y x / \ / \ x c --> a y / \ / \ a b b c */ // rotateRight rotates the tree rooted at node y. // turning (y (x a b) c) into (x a (y b c)). func(root *semaRoot) rotateRight(y *sudog) { // p -> (y (x a b) c) p := y.parent x := y.prev b := x.next
x.next = y y.parent = x y.prev = b if b != nil { b.parent = y }
x.parent = p if p == nil { root.treap = x } elseif p.prev == y { p.prev = x } else { if p.next != y { throw("semaRoot rotateRight") } p.next = x } }
// 唤醒 l 第一个等待者 //go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne funcnotifyListNotifyOne(l *notifyList) { // 已经唤醒过 直接返回 if atomic.Load(&l.wait) == atomic.Load(&l.notify) { return }
lockWithRank(&l.lock, lockRankNotifyList)
// 二次校验是否已经唤醒过 t := l.notify if t == atomic.Load(&l.wait) { unlock(&l.lock) return }
// 增加唤醒标记 atomic.Store(&l.notify, t+1)
// 尝试查找需要唤醒的 sudog // 如果来不及插入此列表就被唤醒 则遍历 sudog 列表不会触发唤醒 但是在插入的时候会直接唤醒 // 不存在表示被唤醒过 for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next { if s.ticket == t { // 从队列中摘除 s n := s.next if p != nil { p.next = n } else { l.head = n } if n == nil { l.tail = p } unlock(&l.lock) s.next = nil // 找到了 唤醒s readyWithTime(s, 4) return } } unlock(&l.lock) }