1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 07:52:50 -05:00

core/wayland: thread-safety meta fixes + cleanups + hypr workaround

- fork go-wayland/client and modify to make it thread-safe internally
- use sync.Map and atomic values in many places to cut down on mutex
  boilerplate
- do not create extworkspace client unless explicitly requested
This commit is contained in:
bbedward
2025-11-15 14:41:00 -05:00
parent 20f7d60147
commit 91891a14ed
54 changed files with 8610 additions and 698 deletions

View File

@@ -35,13 +35,11 @@ func NewManager() (*Manager, error) {
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: client,
baseURL: baseURL,
stateMutex: sync.RWMutex{},
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
subscribers: make(map[string]chan CUPSState),
subMutex: sync.RWMutex{},
client: client,
baseURL: baseURL,
stateMutex: sync.RWMutex{},
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
if err := m.updateState(); err != nil {
@@ -142,28 +140,22 @@ func (m *Manager) notifier() {
if !pending {
continue
}
m.subMutex.RLock()
if len(m.subscribers) == 0 {
m.subMutex.RUnlock()
pending = false
continue
}
currentState := m.snapshotState()
if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, &currentState) {
m.subMutex.RUnlock()
pending = false
continue
}
for _, ch := range m.subscribers {
m.subscribers.Range(func(key, value interface{}) bool {
ch := value.(chan CUPSState)
select {
case ch <- currentState:
default:
}
}
m.subMutex.RUnlock()
return true
})
stateCopy := currentState
m.lastNotifiedState = &stateCopy
@@ -199,10 +191,14 @@ func (m *Manager) snapshotState() CUPSState {
func (m *Manager) Subscribe(id string) chan CUPSState {
ch := make(chan CUPSState, 64)
m.subMutex.Lock()
wasEmpty := len(m.subscribers) == 0
m.subscribers[id] = ch
m.subMutex.Unlock()
wasEmpty := true
m.subscribers.Range(func(key, value interface{}) bool {
wasEmpty = false
return false
})
m.subscribers.Store(id, ch)
if wasEmpty && m.subscription != nil {
if err := m.subscription.Start(); err != nil {
@@ -217,13 +213,15 @@ func (m *Manager) Subscribe(id string) chan CUPSState {
}
func (m *Manager) Unsubscribe(id string) {
m.subMutex.Lock()
if ch, ok := m.subscribers[id]; ok {
close(ch)
delete(m.subscribers, id)
if val, ok := m.subscribers.LoadAndDelete(id); ok {
close(val.(chan CUPSState))
}
isEmpty := len(m.subscribers) == 0
m.subMutex.Unlock()
isEmpty := true
m.subscribers.Range(func(key, value interface{}) bool {
isEmpty = false
return false
})
if isEmpty && m.subscription != nil {
m.subscription.Stop()
@@ -241,12 +239,12 @@ func (m *Manager) Close() {
m.eventWG.Wait()
m.notifierWg.Wait()
m.subMutex.Lock()
for _, ch := range m.subscribers {
m.subscribers.Range(func(key, value interface{}) bool {
ch := value.(chan CUPSState)
close(ch)
}
m.subscribers = make(map[string]chan CUPSState)
m.subMutex.Unlock()
m.subscribers.Delete(key)
return true
})
}
func stateChanged(old, new *CUPSState) bool {

View File

@@ -13,10 +13,9 @@ func TestNewManager(t *testing.T) {
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: nil,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
subscribers: make(map[string]chan CUPSState),
client: nil,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
assert.NotNil(t, m)
@@ -35,10 +34,9 @@ func TestManager_GetState(t *testing.T) {
},
},
},
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
subscribers: make(map[string]chan CUPSState),
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
state := m.GetState()
@@ -53,18 +51,28 @@ func TestManager_Subscribe(t *testing.T) {
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
subscribers: make(map[string]chan CUPSState),
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
ch := m.Subscribe("test-client")
assert.NotNil(t, ch)
assert.Equal(t, 1, len(m.subscribers))
count := 0
m.subscribers.Range(func(key, value interface{}) bool {
count++
return true
})
assert.Equal(t, 1, count)
m.Unsubscribe("test-client")
assert.Equal(t, 0, len(m.subscribers))
count = 0
m.subscribers.Range(func(key, value interface{}) bool {
count++
return true
})
assert.Equal(t, 0, count)
}
func TestManager_Close(t *testing.T) {
@@ -74,10 +82,9 @@ func TestManager_Close(t *testing.T) {
state: &CUPSState{
Printers: make(map[string]*Printer),
},
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
subscribers: make(map[string]chan CUPSState),
client: mockClient,
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
}
m.eventWG.Add(1)
@@ -93,7 +100,12 @@ func TestManager_Close(t *testing.T) {
}()
m.Close()
assert.Equal(t, 0, len(m.subscribers))
count := 0
m.subscribers.Range(func(key, value interface{}) bool {
count++
return true
})
assert.Equal(t, 0, count)
}
func TestStateChanged(t *testing.T) {

View File

@@ -39,8 +39,7 @@ type Manager struct {
client CUPSClientInterface
subscription SubscriptionManagerInterface
stateMutex sync.RWMutex
subscribers map[string]chan CUPSState
subMutex sync.RWMutex
subscribers sync.Map
stopChan chan struct{}
eventWG sync.WaitGroup
dirty chan struct{}