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

@@ -154,14 +154,14 @@ func (m *Manager) ApplyConfiguration(heads []HeadConfig, test bool) error {
statusChan <- fmt.Errorf("configuration cancelled (outdated serial)")
})
m.headsMutex.RLock()
headsByName := make(map[string]*headState)
for _, head := range m.heads {
m.heads.Range(func(key, value interface{}) bool {
head := value.(*headState)
if !head.finished {
headsByName[head.name] = head
}
}
m.headsMutex.RUnlock()
return true
})
for _, headCfg := range heads {
head, exists := headsByName[headCfg.Name]
@@ -188,15 +188,14 @@ func (m *Manager) ApplyConfiguration(heads []HeadConfig, test bool) error {
}
if headCfg.ModeID != nil {
m.modesMutex.RLock()
mode, exists := m.modes[*headCfg.ModeID]
m.modesMutex.RUnlock()
val, exists := m.modes.Load(*headCfg.ModeID)
if !exists {
config.Destroy()
resultChan <- fmt.Errorf("mode not found: %d", *headCfg.ModeID)
return
}
mode := val.(*modeState)
if err := headConfig.SetMode(mode.handle); err != nil {
config.Destroy()

View File

@@ -6,20 +6,17 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management"
wlclient "github.com/yaslama/go-wayland/wayland/client"
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
)
func NewManager(display *wlclient.Display) (*Manager, error) {
m := &Manager{
display: display,
ctx: display.Context(),
heads: make(map[uint32]*headState),
modes: make(map[uint32]*modeState),
cmdq: make(chan cmd, 128),
stopChan: make(chan struct{}),
subscribers: make(map[string]chan State),
dirty: make(chan struct{}, 1),
fatalError: make(chan error, 1),
display: display,
ctx: display.Context(),
cmdq: make(chan cmd, 128),
stopChan: make(chan struct{}),
dirty: make(chan struct{}, 1),
fatalError: make(chan error, 1),
}
m.wg.Add(1)
@@ -143,9 +140,7 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
modeIDs: make([]uint32, 0),
}
m.headsMutex.Lock()
m.heads[headID] = head
m.headsMutex.Unlock()
m.heads.Store(headID, head)
handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) {
log.Debugf("WlrOutput: Head %d name: %s", headID, e.Name)
@@ -254,9 +249,7 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
log.Debugf("WlrOutput: Head %d finished", headID)
head.finished = true
m.headsMutex.Lock()
delete(m.heads, headID)
m.headsMutex.Unlock()
m.heads.Delete(headID)
m.post(func() {
m.wlMutex.Lock()
@@ -279,15 +272,13 @@ func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHe
handle: handle,
}
m.modesMutex.Lock()
m.modes[modeID] = mode
m.modesMutex.Unlock()
m.modes.Store(modeID, mode)
m.headsMutex.Lock()
if head, ok := m.heads[headID]; ok {
if val, ok := m.heads.Load(headID); ok {
head := val.(*headState)
head.modeIDs = append(head.modeIDs, modeID)
m.heads.Store(headID, head)
}
m.headsMutex.Unlock()
handle.SetSizeHandler(func(e wlr_output_management.ZwlrOutputModeV1SizeEvent) {
log.Debugf("WlrOutput: Mode %d size: %dx%d", modeID, e.Width, e.Height)
@@ -318,9 +309,7 @@ func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHe
log.Debugf("WlrOutput: Mode %d finished", modeID)
mode.finished = true
m.modesMutex.Lock()
delete(m.modes, modeID)
m.modesMutex.Unlock()
m.modes.Delete(modeID)
m.post(func() {
m.wlMutex.Lock()
@@ -333,22 +322,24 @@ func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHe
}
func (m *Manager) updateState() {
m.headsMutex.RLock()
m.modesMutex.RLock()
outputs := make([]Output, 0)
for _, head := range m.heads {
m.heads.Range(func(key, value interface{}) bool {
head := value.(*headState)
if head.finished {
continue
return true
}
modes := make([]OutputMode, 0)
var currentMode *OutputMode
for _, modeID := range head.modeIDs {
mode, exists := m.modes[modeID]
if !exists || mode.finished {
val, exists := m.modes.Load(modeID)
if !exists {
continue
}
mode := val.(*modeState)
if mode.finished {
continue
}
@@ -385,10 +376,8 @@ func (m *Manager) updateState() {
ID: head.id,
}
outputs = append(outputs, output)
}
m.modesMutex.RUnlock()
m.headsMutex.RUnlock()
return true
})
newState := State{
Outputs: outputs,
@@ -442,14 +431,6 @@ func (m *Manager) notifier() {
if !pending {
continue
}
m.subMutex.RLock()
subCount := len(m.subscribers)
m.subMutex.RUnlock()
if subCount == 0 {
pending = false
continue
}
currentState := m.GetState()
@@ -458,15 +439,15 @@ func (m *Manager) notifier() {
continue
}
m.subMutex.RLock()
for _, ch := range m.subscribers {
m.subscribers.Range(func(key, value interface{}) bool {
ch := value.(chan State)
select {
case ch <- currentState:
default:
log.Warn("WlrOutput: subscriber channel full, dropping update")
}
}
m.subMutex.RUnlock()
return true
})
stateCopy := currentState
m.lastNotified = &stateCopy
@@ -480,30 +461,30 @@ func (m *Manager) Close() {
m.wg.Wait()
m.notifierWg.Wait()
m.subMutex.Lock()
for _, ch := range m.subscribers {
m.subscribers.Range(func(key, value interface{}) bool {
ch := value.(chan State)
close(ch)
}
m.subscribers = make(map[string]chan State)
m.subMutex.Unlock()
m.subscribers.Delete(key)
return true
})
m.modesMutex.Lock()
for _, mode := range m.modes {
m.modes.Range(func(key, value interface{}) bool {
mode := value.(*modeState)
if mode.handle != nil {
mode.handle.Release()
}
}
m.modes = make(map[uint32]*modeState)
m.modesMutex.Unlock()
m.modes.Delete(key)
return true
})
m.headsMutex.Lock()
for _, head := range m.heads {
m.heads.Range(func(key, value interface{}) bool {
head := value.(*headState)
if head.handle != nil {
head.handle.Release()
}
}
m.heads = make(map[uint32]*headState)
m.headsMutex.Unlock()
m.heads.Delete(key)
return true
})
if m.manager != nil {
m.manager.Stop()

View File

@@ -4,7 +4,7 @@ import (
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management"
wlclient "github.com/yaslama/go-wayland/wayland/client"
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
)
type OutputMode struct {
@@ -49,11 +49,8 @@ type Manager struct {
registry *wlclient.Registry
manager *wlr_output_management.ZwlrOutputManagerV1
headsMutex sync.RWMutex
heads map[uint32]*headState
modesMutex sync.RWMutex
modes map[uint32]*modeState
heads sync.Map // map[uint32]*headState
modes sync.Map // map[uint32]*modeState
serial uint32
@@ -62,8 +59,7 @@ type Manager struct {
stopChan chan struct{}
wg sync.WaitGroup
subscribers map[string]chan State
subMutex sync.RWMutex
subscribers sync.Map
dirty chan struct{}
notifierWg sync.WaitGroup
lastNotified *State
@@ -120,19 +116,19 @@ func (m *Manager) GetState() State {
func (m *Manager) Subscribe(id string) chan State {
ch := make(chan State, 64)
m.subMutex.Lock()
m.subscribers[id] = ch
m.subMutex.Unlock()
m.subscribers.Store(id, ch)
return ch
}
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 State))
}
m.subMutex.Unlock()
}
func (m *Manager) notifySubscribers() {