mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-05-22 10:15:43 +02:00
5928e851dd
Bumps [github.com/sasha-s/go-deadlock](https://github.com/sasha-s/go-deadlock) from 0.3.6 to 0.3.9. - [Release notes](https://github.com/sasha-s/go-deadlock/releases) - [Commits](https://github.com/sasha-s/go-deadlock/compare/v0.3.6...v0.3.9) --- updated-dependencies: - dependency-name: github.com/sasha-s/go-deadlock dependency-version: 0.3.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
235 lines
4.9 KiB
Go
235 lines
4.9 KiB
Go
//go:build (goexperiment.synctest || deadlock_synctest) && !deadlock_disable
|
|
|
|
package deadlock
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// ChannelMutex implements MutexImpl using channels for synctest compatibility
|
|
type ChannelMutex struct {
|
|
ch chan struct{}
|
|
locked int32 // atomic
|
|
once sync.Once
|
|
}
|
|
|
|
func (m *ChannelMutex) init() {
|
|
m.once.Do(func() {
|
|
m.ch = make(chan struct{}, 1)
|
|
})
|
|
}
|
|
|
|
func (m *ChannelMutex) Lock() {
|
|
m.init()
|
|
m.ch <- struct{}{}
|
|
atomic.StoreInt32(&m.locked, 1)
|
|
}
|
|
|
|
func (m *ChannelMutex) Unlock() {
|
|
if atomic.LoadInt32(&m.locked) == 0 {
|
|
panic("unlock of unlocked mutex")
|
|
}
|
|
atomic.StoreInt32(&m.locked, 0)
|
|
<-m.ch
|
|
}
|
|
|
|
func (m *ChannelMutex) TryLock() bool {
|
|
m.init()
|
|
select {
|
|
case m.ch <- struct{}{}:
|
|
atomic.StoreInt32(&m.locked, 1)
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// ChannelRWMutex implements RWMutexImpl with writer priority using channels.
|
|
// Implements writer-priority semantics using the "Third Readers-Writers Problem" solution.
|
|
type ChannelRWMutex struct {
|
|
resource chan struct{} // The actual resource being protected
|
|
readTry chan struct{} // Gate that closes when writers waiting
|
|
rmutex chan struct{} // Protects readCount modifications
|
|
wmutex chan struct{} // Protects writeCount modifications
|
|
readCount int32 // Number of active readers
|
|
writeCount int32 // Number of waiting/active writers
|
|
once sync.Once
|
|
}
|
|
|
|
func (m *ChannelRWMutex) init() {
|
|
m.once.Do(func() {
|
|
m.resource = make(chan struct{}, 1)
|
|
m.readTry = make(chan struct{}, 1)
|
|
m.rmutex = make(chan struct{}, 1)
|
|
m.wmutex = make(chan struct{}, 1)
|
|
// Initially, all semaphores are "released" (have a token)
|
|
m.resource <- struct{}{}
|
|
m.readTry <- struct{}{}
|
|
m.rmutex <- struct{}{}
|
|
m.wmutex <- struct{}{}
|
|
})
|
|
}
|
|
|
|
func (m *ChannelRWMutex) Lock() {
|
|
m.init()
|
|
// Protect writeCount modification
|
|
<-m.wmutex
|
|
count := atomic.AddInt32(&m.writeCount, 1)
|
|
if count == 1 {
|
|
// First writer: close the gate to block new readers
|
|
<-m.readTry
|
|
}
|
|
m.wmutex <- struct{}{} // Release wmutex
|
|
|
|
// Acquire the resource (wait for existing readers to finish)
|
|
<-m.resource
|
|
}
|
|
|
|
func (m *ChannelRWMutex) Unlock() {
|
|
// Release the resource
|
|
m.resource <- struct{}{}
|
|
|
|
// Protect writeCount modification
|
|
<-m.wmutex
|
|
count := atomic.AddInt32(&m.writeCount, -1)
|
|
if count == 0 {
|
|
// Last writer: reopen the gate for readers
|
|
m.readTry <- struct{}{}
|
|
}
|
|
m.wmutex <- struct{}{}
|
|
}
|
|
|
|
func (m *ChannelRWMutex) RLock() {
|
|
m.init()
|
|
// Wait at the gate (blocks if writers are waiting)
|
|
<-m.readTry
|
|
|
|
// Protect readCount modification
|
|
<-m.rmutex
|
|
count := atomic.AddInt32(&m.readCount, 1)
|
|
if count == 1 {
|
|
// First reader: acquire the resource to block writers
|
|
<-m.resource
|
|
}
|
|
m.rmutex <- struct{}{} // Release rmutex
|
|
|
|
// Release the gate so other readers can pass
|
|
m.readTry <- struct{}{}
|
|
}
|
|
|
|
func (m *ChannelRWMutex) RUnlock() {
|
|
<-m.rmutex
|
|
count := atomic.AddInt32(&m.readCount, -1)
|
|
if count < 0 {
|
|
m.rmutex <- struct{}{}
|
|
panic("RUnlock of unlocked RWMutex")
|
|
}
|
|
if count == 0 {
|
|
// Last reader: release the resource for writers
|
|
m.resource <- struct{}{}
|
|
}
|
|
m.rmutex <- struct{}{}
|
|
}
|
|
|
|
func (m *ChannelRWMutex) TryLock() bool {
|
|
m.init()
|
|
// Try to acquire wmutex
|
|
select {
|
|
case <-m.wmutex:
|
|
default:
|
|
return false
|
|
}
|
|
|
|
count := atomic.AddInt32(&m.writeCount, 1)
|
|
if count == 1 {
|
|
// First writer: try to close gate
|
|
select {
|
|
case <-m.readTry:
|
|
default:
|
|
// Failed, rollback
|
|
atomic.AddInt32(&m.writeCount, -1)
|
|
m.wmutex <- struct{}{}
|
|
return false
|
|
}
|
|
}
|
|
m.wmutex <- struct{}{}
|
|
|
|
// Try to acquire resource
|
|
select {
|
|
case <-m.resource:
|
|
return true
|
|
default:
|
|
// Failed, rollback writer count
|
|
<-m.wmutex
|
|
count = atomic.AddInt32(&m.writeCount, -1)
|
|
if count == 0 {
|
|
m.readTry <- struct{}{}
|
|
}
|
|
m.wmutex <- struct{}{}
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (m *ChannelRWMutex) TryRLock() bool {
|
|
m.init()
|
|
// Try to pass through the gate
|
|
select {
|
|
case <-m.readTry:
|
|
default:
|
|
return false
|
|
}
|
|
|
|
// Try to acquire rmutex
|
|
select {
|
|
case <-m.rmutex:
|
|
default:
|
|
// Failed, release gate
|
|
m.readTry <- struct{}{}
|
|
return false
|
|
}
|
|
|
|
count := atomic.AddInt32(&m.readCount, 1)
|
|
if count == 1 {
|
|
// First reader: try to acquire resource
|
|
select {
|
|
case <-m.resource:
|
|
default:
|
|
// Failed, rollback
|
|
atomic.AddInt32(&m.readCount, -1)
|
|
m.rmutex <- struct{}{}
|
|
m.readTry <- struct{}{}
|
|
return false
|
|
}
|
|
}
|
|
m.rmutex <- struct{}{}
|
|
m.readTry <- struct{}{}
|
|
return true
|
|
}
|
|
|
|
func (m *ChannelRWMutex) RLocker() sync.Locker {
|
|
return (*channelRLocker)(m)
|
|
}
|
|
|
|
type channelRLocker ChannelRWMutex
|
|
|
|
func (r *channelRLocker) Lock() { (*ChannelRWMutex)(r).RLock() }
|
|
func (r *channelRLocker) Unlock() { (*ChannelRWMutex)(r).RUnlock() }
|
|
|
|
// Factory functions for synctest
|
|
func newChannelMutex() MutexImpl {
|
|
return &ChannelMutex{
|
|
ch: make(chan struct{}, 1),
|
|
}
|
|
}
|
|
|
|
func newChannelRWMutex() RWMutexImpl {
|
|
m := &ChannelRWMutex{}
|
|
m.init()
|
|
return m
|
|
}
|
|
|
|
// Type aliases to override the standard mutex types for synctest
|
|
type StandardMutex = ChannelMutex
|
|
type StandardRWMutex = ChannelRWMutex
|