Deadlock fix: no channel sends during frame rendering while mutex is locked.
This commit is contained in:
parent
b838b4c75f
commit
367e6e9052
|
|
@ -29,13 +29,16 @@ func main() {
|
|||
}
|
||||
|
||||
func run(w *app.Window) error {
|
||||
log.Printf("run: starting")
|
||||
var ops op.Ops
|
||||
shaper := text.NewShaper(text.WithCollection(gofont.Collection()))
|
||||
logic := editor.NewLogic()
|
||||
renderer := ui.New(ui.Theme{FontSize: 14}, shaper, logic.State())
|
||||
var mu sync.Mutex
|
||||
var elems []ui.Element
|
||||
log.Printf("run: starting frameReceiver")
|
||||
go frameReceiver(w, &mu, &elems, logic.FrameChan())
|
||||
log.Printf("run: starting logic.Run")
|
||||
go logic.Run()
|
||||
|
||||
for {
|
||||
|
|
@ -49,34 +52,43 @@ func run(w *app.Window) error {
|
|||
PixelHeight: e.Config.Size.Y,
|
||||
}
|
||||
case app.FrameEvent:
|
||||
log.Printf("run: FrameEvent")
|
||||
gtx := app.NewContext(&ops, e)
|
||||
newScale := gtx.Metric.PxPerDp
|
||||
curScale := logic.State().Scale()
|
||||
if newScale != curScale {
|
||||
logic.ConfigChan() <- editor.ScaleEvent{newScale}
|
||||
}
|
||||
// Deadlock risk: ensure no channel sends while lock is held
|
||||
mu.Lock()
|
||||
currentElems := elems
|
||||
renderer.Draw(gtx, currentElems)
|
||||
logic.DisplayLineChan() <- int(renderer.LastLineY())
|
||||
lastLineY := int(renderer.LastLineY())
|
||||
// Send search query update to the logic goroutine when it changes.
|
||||
// The logic goroutine handles filtering and triggers a new frame.
|
||||
newQuery := logic.State().Browser.SearchEditor.Text()
|
||||
sendQuery := false
|
||||
if newQuery != logic.State().Browser.Query {
|
||||
logic.SearchQueryChan() <- newQuery
|
||||
sendQuery = true
|
||||
}
|
||||
if events := renderer.CheckGestures(e.Source, gtx.Metric); len(events) > 0 {
|
||||
logic.InputChan() <- events
|
||||
}
|
||||
e.Frame(&ops)
|
||||
mu.Unlock()
|
||||
if sendQuery {
|
||||
logic.SearchQueryChan() <- newQuery
|
||||
}
|
||||
logic.DisplayLineChan() <- lastLineY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func frameReceiver(w *app.Window, mu *sync.Mutex, elems *[]ui.Element, frameChan <-chan []ui.Element) {
|
||||
log.Printf("frameReceiver: loop starting")
|
||||
for {
|
||||
frame := <-frameChan
|
||||
log.Printf("frameReceiver: received frame")
|
||||
mu.Lock()
|
||||
*elems = frame
|
||||
w.Invalidate()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package editor
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"pad/internal/browser"
|
||||
|
|
@ -136,6 +137,7 @@ func (l *Logic) Scale() float32 {
|
|||
// Run runs the logic goroutine loop.
|
||||
func (l *Logic) Run() {
|
||||
// Dispatch initial directory index build on startup
|
||||
log.Printf("Logic: Dispatching BuildIndexTask")
|
||||
l.workerPool.Dispatch(pool.NewBuildIndexTask(l.state.Browser.CurrentPath, l.mockFS))
|
||||
|
||||
for {
|
||||
|
|
@ -143,6 +145,7 @@ func (l *Logic) Run() {
|
|||
case <-l.done:
|
||||
return
|
||||
case update := <-l.configChan:
|
||||
log.Printf("Logic: ConfigEvent")
|
||||
update.apply(l.state)
|
||||
l.frameChan <- l.state.layout(l.browserManager)
|
||||
case y := <-l.lastLineYChan:
|
||||
|
|
@ -151,11 +154,13 @@ func (l *Logic) Run() {
|
|||
l.frameChan <- l.state.layout(l.browserManager)
|
||||
}
|
||||
case events := <-l.inputChan:
|
||||
log.Printf("Logic: InputEvents")
|
||||
for _, evt := range events {
|
||||
evt.Handler(evt.Data)
|
||||
}
|
||||
l.frameChan <- l.state.layout(l.browserManager)
|
||||
case query := <-l.searchQueryChan:
|
||||
log.Printf("Logic: SearchQuery")
|
||||
if query != l.state.Browser.Query {
|
||||
l.state.Browser.Query = query
|
||||
if l.state.page == BrowserPage {
|
||||
|
|
@ -164,9 +169,11 @@ func (l *Logic) Run() {
|
|||
}
|
||||
l.frameChan <- l.state.layout(l.browserManager)
|
||||
case path := <-l.openFileChan:
|
||||
log.Printf("Logic: OpenFileChan %s", path)
|
||||
// Dispatch ReadFileTask to worker pool
|
||||
l.workerPool.Dispatch(pool.NewReadFileTask(path, l.mockFS))
|
||||
case res := <-l.workerPool.ResultChan():
|
||||
log.Printf("Logic: WorkerResult %s", res.TaskType)
|
||||
l.handleWorkerResult(res)
|
||||
case <-l.resultChan:
|
||||
l.frameChan <- l.state.layout(l.browserManager)
|
||||
|
|
@ -179,6 +186,7 @@ func (l *Logic) handleWorkerResult(res pool.Result) {
|
|||
if res.IsBrowserResult() {
|
||||
l.browserManager.HandleResult(res)
|
||||
} else if res.TaskType == pool.TypeReadFile {
|
||||
log.Printf("Logic: TypeReadFile result success=%v", res.Success)
|
||||
if res.Success {
|
||||
if content, ok := res.Data.([]byte); ok {
|
||||
l.state.ActiveFileContent = string(content)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user