golang底层 note mutex semaphone

 

futex
futex(&f, FUTEX_WAIT, val, t, nil, 0) 
选项FUTEX_WAIT,表示使用mmap在内核态和用户态之间共享f的内存,测试f的值如果等于val则休眠时间t
futex(&f, FUTEX_WAKE, cnt, nil, nil, 0)
选项FUTEX_WAKE,表示唤醒阻塞在f上的cnt个线程

--------------------------------------------------------------------------------
note
type note struct { key uintptr }
func notesleep(n *note) {
    gp := getg()
    if gp != gp.m.g0 { throw("notesleep not on g0") }
    ns := int64(-1)
    for atomic.Load(key32(&n.key)) == 0 {    // 用户态测试,不需要陷入内核
        gp.m.blocked = true
        futexsleep(key32(&n.key), 0, ns)    // 内核态测试并休眠
        gp.m.blocked = false
    }
}
func notewakeup(n *note) {
    old := atomic.Xchg(key32(&n.key), 1)
    if old != 0 { throw("notewakeup - double wakeup") }
    futexwakeup(key32(&n.key), 1)
}

--------------------------------------------------------------------------------
mutex
type mutex struct { key uintptr }
const (
    mutex_unlocked = 0
    mutex_locked   = 1
    mutex_sleeping = 2
    active_spin     = 4
    passive_spin    = 1
)
func lock(l *mutex) {    // 省略大量无关代码
    v := atomic.Xchg(key32(&l.key), mutex_locked)
    if v == mutex_unlocked { return }    // 未上锁 -> 上锁
    wait := v
    for {
        for i := 0; i < spin; i++ {    // 尝试加锁, spinning
            for l.key == mutex_unlocked {
                if atomic.Cas(key32(&l.key), mutex_unlocked, wait) {
                    return
                }
            }
            procyield(active_spin_cnt) // 30次PAUSE指令
        }
        v = atomic.Xchg(key32(&l.key), mutex_sleeping)
        if v == mutex_unlocked { return }
        wait = mutex_sleeping
        futexsleep(key32(&l.key), mutex_sleeping, -1)
    }
}
func unlock(l *mutex) {
    v := atomic.Xchg(key32(&l.key), mutex_unlocked)
    if v == mutex_sleeping { futexwakeup(key32(&l.key), 1) }
}

--------------------------------------------------------------------------------
semaphore
type semaRoot struct {
    lock      mutex
    treap     *sudog     // treap的根
    nwait     uint32     // Number of waiters
}

const  semTabSize = 251
var semtable [semTabSize]struct { root semaRoot }    // 数组,每个元素都是treap的根

func semroot(addr *uint32) *semaRoot {    // 通过sema的地址,找到对应的treap
    return     &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
}

type sudog struct {    
  g *g
   isSelect bool  // 是否参与select
   next     *sudog    // treap的右孩子
   prev     *sudog    // treap的左孩子
   elem     unsafe.Pointer // sema的地址addr

   acquiretime int64
   releasetime int64
   ticket      uint32    // treap随机值
   parent      *sudog     // treap的parent
   waitlink    *sudog     // 相同地址链表的下一个结点
   waittail    *sudog     // 相同地址链表的尾结点
   c           *hchan // channel
}
sudog也是先从p的本地缓存获取,本地没有则到全局拿一半过来,全局也没有则创建,本地太多了则把一半放到全局缓存里。

信号量按addr的哈希值被组织成251颗treap,
不同的addr满足二叉搜索树的性质,而随机生成的ticket满足小顶堆的性质
相同的addr通过waitlink链接成链表

semacquire 如果*addr不为0,则将addr减1,返回。否则将g包装成sudog,放到treap里          goparkunlock

semrelease 将*addr加1,然后从treap里取出一个sudog,将里面的g放到等待队列里    goready

 

posted @ 2020-05-27 22:37  是的哟  阅读(326)  评论(0编辑  收藏  举报