1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2026-05-22 10:15:43 +02:00
Files
lazygit/vendor/github.com/sasha-s/go-deadlock/synctest_mutex.go
T
dependabot[bot] 5928e851dd Bump github.com/sasha-s/go-deadlock from 0.3.6 to 0.3.9
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>
2026-04-01 09:24:57 +00:00

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