sync.Mutex
type Mutex struct {
_ noCopy
mu isync.Mutex
}
type Mutex struct {
state int32
sema uint32
}
const (
mutexLocked = 1 << 0
mutexWoken = 1 << 1
mutexStarving = 1 << 2
mutexWaiterShift = 3
)
Lock
- 快速路径:当前 state == 0(没人持锁、没人等),CAS 成 mutexLocked 就拿到锁,直接 return。
- 否则进 lockSlow(),里面做自旋、排队、饥饿切换等。
func (m *Mutex) Lock() {
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
...
return
}
m.lockSlow()
}
func (m *Mutex) lockSlow() {
...
for {
if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
awoke = true
}
runtime_doSpin()
iter++
old = m.state
continue
}
new := old
if old&mutexStarving == 0 {
new |= mutexLocked
}
if old&(mutexLocked|mutexStarving) != 0 {
new += 1 << mutexWaiterShift
}
if starving && old&mutexLocked != 0 {
new |= mutexStarving
}
...
if atomic.CompareAndSwapInt32(&m.state, old, new) {
if old&(mutexLocked|mutexStarving) == 0 {
break
}
queueLifo := waitStartTime != 0
if waitStartTime == 0 {
waitStartTime = runtime_nanotime()
}
runtime_SemacquireMutex(&m.sema, queueLifo, 2)
starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
old = m.state
if old&mutexStarving != 0 {
...
delta := int32(mutexLocked - 1<<mutexWaiterShift)
if !starving || old>>mutexWaiterShift == 1 {
delta -= mutexStarving
}
atomic.AddInt32(&m.state, delta)
break
}
awoke = true
iter = 0
} else {
old = m.state
}
}
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
}
TryLock
- 若已经加锁或处于饥饿(饥饿时锁要交给 waiter,不能给 TryLock),直接返回 false。
- 否则 CAS 把 mutexLocked 置 1,成功返回 true,失败返回 false。不排队、不自旋。
func (m *Mutex) TryLock() bool {
old := m.state
if old&(mutexLocked|mutexStarving) != 0 {
return false
}
if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {
return false
}
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return true
}
UnLock
func (m *Mutex) Unlock() {
if race.Enabled {
_ = m.state
race.Release(unsafe.Pointer(m))
}
new := atomic.AddInt32(&m.state, -mutexLocked)
if new != 0 {
m.unlockSlow(new)
}
}
- 检查:若 (new+mutexLocked)&mutexLocked == 0,说明本来就没锁,fatal("unlock of unlocked mutex")。
- 非饥饿:
- 若没有 waiter,或已经有别人把锁/唤醒拿走了,直接 return。
- 否则 CAS:waiter 数减 1、置上 mutexWoken,成功则 runtime_Semrelease(&m.sema, ...) 唤醒一个 waiter。
- 饥饿:
func (m *Mutex) unlockSlow(new int32) {
if (new+mutexLocked)&mutexLocked == 0 {
fatal("sync: unlock of unlocked mutex")
}
if new&mutexStarving == 0 {
old := new
for {
if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
return
}
new = (old - 1<<mutexWaiterShift) | mutexWoken
if atomic.CompareAndSwapInt32(&m.state, old, new) {
runtime_Semrelease(&m.sema, false, 2)
return
}
old = m.state
}
} else {
runtime_Semrelease(&m.sema, true, 2)
}
}