Этот коммит содержится в:
soypat 2023-11-01 21:16:26 -03:00 коммит произвёл Ayke
родитель 45764325b4
коммит cb39611389
2 изменённых файлов: 71 добавлений и 5 удалений

Просмотреть файл

@ -3,10 +3,12 @@ package sync
import ( import (
"internal/task" "internal/task"
_ "unsafe" _ "unsafe"
"runtime/volatile"
) )
type Mutex struct { type Mutex struct {
locked bool state uint8 // Set to non-zero if locked.
blocked task.Stack blocked task.Stack
} }
@ -14,18 +16,18 @@ type Mutex struct {
func scheduleTask(*task.Task) func scheduleTask(*task.Task)
func (m *Mutex) Lock() { func (m *Mutex) Lock() {
if m.locked { if m.islocked() {
// Push self onto stack of blocked tasks, and wait to be resumed. // Push self onto stack of blocked tasks, and wait to be resumed.
m.blocked.Push(task.Current()) m.blocked.Push(task.Current())
task.Pause() task.Pause()
return return
} }
m.locked = true m.setlock(true)
} }
func (m *Mutex) Unlock() { func (m *Mutex) Unlock() {
if !m.locked { if !m.islocked() {
panic("sync: unlock of unlocked Mutex") panic("sync: unlock of unlocked Mutex")
} }
@ -33,10 +35,38 @@ func (m *Mutex) Unlock() {
if t := m.blocked.Pop(); t != nil { if t := m.blocked.Pop(); t != nil {
scheduleTask(t) scheduleTask(t)
} else { } else {
m.locked = false m.setlock(false)
} }
} }
// TryLock tries to lock m and reports whether it succeeded.
//
// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
// in a particular use of mutexes.
func (m *Mutex) TryLock() bool {
if m.islocked() {
return false
}
m.Lock()
return true
}
func (m *Mutex) islocked() bool {
return volatile.LoadUint8(&m.state) != 0
}
func (m *Mutex) setlock(b bool) {
volatile.StoreUint8(&m.state, boolToU8(b))
}
func boolToU8(b bool) uint8 {
if b {
return 1
}
return 0
}
type RWMutex struct { type RWMutex struct {
// waitingWriters are all of the tasks waiting for write locks. // waitingWriters are all of the tasks waiting for write locks.
waitingWriters task.Stack waitingWriters task.Stack

Просмотреть файл

@ -7,6 +7,42 @@ import (
"testing" "testing"
) )
func HammerMutex(m *sync.Mutex, loops int, cdone chan bool) {
for i := 0; i < loops; i++ {
if i%3 == 0 {
if m.TryLock() {
m.Unlock()
}
continue
}
m.Lock()
m.Unlock()
}
cdone <- true
}
func TestMutex(t *testing.T) {
m := new(sync.Mutex)
m.Lock()
if m.TryLock() {
t.Fatalf("TryLock succeeded with mutex locked")
}
m.Unlock()
if !m.TryLock() {
t.Fatalf("TryLock failed with mutex unlocked")
}
m.Unlock()
c := make(chan bool)
for i := 0; i < 10; i++ {
go HammerMutex(m, 1000, c)
}
for i := 0; i < 10; i++ {
<-c
}
}
// TestMutexUncontended tests locking and unlocking a Mutex that is not shared with any other goroutines. // TestMutexUncontended tests locking and unlocking a Mutex that is not shared with any other goroutines.
func TestMutexUncontended(t *testing.T) { func TestMutexUncontended(t *testing.T) {
var mu sync.Mutex var mu sync.Mutex