Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Creating a version of that does not wait
Naming and implementation open for discussion
andydotxyz committed Jan 21, 2025
commit 1a9c341a1f43aef0fae3b376155bbcb068f27d8a
6 changes: 3 additions & 3 deletions driver.go
Original file line number Diff line number Diff line change
@@ -47,11 +47,11 @@ type Driver interface {
// Since: 2.5
SetDisableScreenBlanking(bool)

// DoFromGoroutine provides a way to queue a function that is running on a goroutine back to
// the central thread for Fyne updates.
// DoFromGoroutine provides a way to queue a function `fn` that is running on a goroutine back to
// the central thread for Fyne updates, waiting for it to return if `wait` is true.
// The driver provides the implementation normally accessed through [fyne.Do].
// This is required when background tasks want to execute code safely in the graphical context.
//
// Since: 2.6
DoFromGoroutine(func())
DoFromGoroutine(fn func(), wait bool)
}
4 changes: 2 additions & 2 deletions internal/app/lifecycle.go
Original file line number Diff line number Diff line change
@@ -106,9 +106,9 @@ func (l *Lifecycle) QueueEvent(fn func()) {

// RunEventQueue runs the event queue. This should called inside a go routine.
// This function blocks.
func (l *Lifecycle) RunEventQueue(run func(func())) {
func (l *Lifecycle) RunEventQueue(run func(func(), bool)) {
for fn := range l.eventQueue.Out() {
run(fn)
run(fn, true)
}
}

4 changes: 2 additions & 2 deletions internal/driver/glfw/driver.go
Original file line number Diff line number Diff line change
@@ -56,9 +56,9 @@ func toOSIcon(icon []byte) ([]byte, error) {
return buf.Bytes(), nil
}

func (d *gLDriver) DoFromGoroutine(f func()) {
func (d *gLDriver) DoFromGoroutine(f func(), wait bool) {
async.EnsureNotMain(func() {
runOnMain(f)
runOnMainWithWait(f, wait)
})
}

21 changes: 16 additions & 5 deletions internal/driver/glfw/loop.go
Original file line number Diff line number Diff line change
@@ -31,19 +31,28 @@ func init() {

// force a function f to run on the main thread
func runOnMain(f func()) {
runOnMainWithWait(f, true)
}

// force a function f to run on the main thread and specify if we should wait for it to return
func runOnMainWithWait(f func(), wait bool) {
// If we are on main just execute - otherwise add it to the main queue and wait.
// The "running" variable is normally false when we are on the main thread.
if !running.Load() {
f()
return
}

done := common.DonePool.Get()
defer common.DonePool.Put(done)
var done chan struct{}
if wait {
done = common.DonePool.Get()
defer common.DonePool.Put(done)
}

funcQueue <- funcData{f: f, done: done}

<-done
if wait {
<-done
}
}

// Preallocate to avoid allocations on every drawSingleFrame.
@@ -110,7 +119,9 @@ func (d *gLDriver) runGL() {
return
case f := <-funcQueue:
f.f()
f.done <- struct{}{}
if f.done != nil {
f.done <- struct{}{}
}
case <-eventTick.C:
d.pollEvents()
for i := 0; i < len(d.windows); i++ {
6 changes: 1 addition & 5 deletions internal/driver/glfw/window.go
Original file line number Diff line number Diff line change
@@ -867,11 +867,7 @@ func (w *window) Context() any {

func (w *window) runOnMainWhenCreated(fn func()) {
if w.view() != nil {
if async.IsMainGoroutine() {
fn()
return
}
fyne.Do(fn)
async.EnsureMain(fn)
return
}

8 changes: 4 additions & 4 deletions internal/driver/mobile/canvas_test.go
Original file line number Diff line number Diff line change
@@ -142,7 +142,7 @@ func Test_canvas_Focusable(t *testing.T) {
c.tapUp(pos, 0, func(wid fyne.Tappable, ev *fyne.PointEvent) {
wid.Tapped(ev)
}, nil, nil, nil)
})
}, true)

waitAndCheck(tapDoubleDelay/time.Millisecond+150, func() {
assert.Equal(t, 1, content.focusedTimes)
@@ -154,7 +154,7 @@ func Test_canvas_Focusable(t *testing.T) {
c.tapUp(pos, 1, func(wid fyne.Tappable, ev *fyne.PointEvent) {
wid.Tapped(ev)
}, nil, nil, nil)
})
}, true)
waitAndCheck(tapDoubleDelay/time.Millisecond+150, func() {
assert.Equal(t, 1, content.focusedTimes)
assert.Equal(t, 0, content.unfocusedTimes)
@@ -177,7 +177,7 @@ func Test_canvas_Focusable(t *testing.T) {
c.tapDown(fyne.NewPos(10, 10), 2)
assert.Equal(t, 1, content.focusedTimes)
assert.Equal(t, 1, content.unfocusedTimes)
})
}, true)
}

func Test_canvas_InteractiveArea(t *testing.T) {
@@ -534,7 +534,7 @@ func waitAndCheck(msWait time.Duration, fn func()) {
fn()

waitForCheck <- struct{}{}
})
}, true)
}()
<-waitForCheck
}
21 changes: 14 additions & 7 deletions internal/driver/mobile/driver.go
Original file line number Diff line number Diff line change
@@ -72,17 +72,24 @@ func init() {
runtime.LockOSThread()
}

func (d *driver) DoFromGoroutine(fn func()) {
func (d *driver) DoFromGoroutine(fn func(), wait bool) {
async.EnsureNotMain(func() {
done := common.DonePool.Get()
defer common.DonePool.Put(done)
var done chan struct{}
if wait {
done = common.DonePool.Get()
defer common.DonePool.Put(done)
}

d.queuedFuncs.In() <- func() {
fn()
done <- struct{}{}
if wait {
done <- struct{}{}
}
}

<-done
if wait {
<-done
}
})
}

@@ -446,11 +453,11 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) {

d.DoFromGoroutine(func() {
wid.Dragged(ev)
})
}, true)
time.Sleep(time.Millisecond * 16)
}

d.DoFromGoroutine(wid.DragEnd)
d.DoFromGoroutine(wid.DragEnd, true)
}()
})
}
2 changes: 1 addition & 1 deletion test/app.go
Original file line number Diff line number Diff line change
@@ -253,7 +253,7 @@ func (s *testSettings) apply() {
cache.ResetThemeCaches()
intapp.ApplySettings(s, s.app)
s.app.propertyLock.Unlock()
})
}, false)

s.app.propertyLock.Lock()
s.app.appliedTheme = s.Theme()
3 changes: 2 additions & 1 deletion test/driver.go
Original file line number Diff line number Diff line change
@@ -50,7 +50,8 @@ func NewDriverWithPainter(painter SoftwarePainter) fyne.Driver {
return &driver{painter: painter}
}

func (d *driver) DoFromGoroutine(f func()) {
// DoFromGoroutine on a test driver ignores the wait flag as our threading is simple
func (d *driver) DoFromGoroutine(f func(), _ bool) {
// Tests all run on a single (but potentially different per-test) thread
async.EnsureNotMain(f)
}
12 changes: 11 additions & 1 deletion thread.go
Original file line number Diff line number Diff line change
@@ -6,5 +6,15 @@ package fyne
//
// Since: 2.6
func Do(fn func()) {
CurrentApp().Driver().DoFromGoroutine(fn)
CurrentApp().Driver().DoFromGoroutine(fn, true)
}

// DoAsync is used to execute a specified function in the main Fyne runtime context without waiting.
// This is required when a background process wishes to adjust graphical elements of a running app.
// Developers should use this only from within goroutines they have created and when the result does not have to
// be waited for.
//
// Since: 2.6
func DoAsync(fn func()) {
CurrentApp().Driver().DoFromGoroutine(fn, false)
}

Unchanged files with check annotations Beta

var entered, exited, start, stop, hookedStop, called bool
life.InitEventQueue()
go life.RunEventQueue(func(fn func()) {

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / mobile_tests (1.19.x)

cannot use func(fn func()) {…} (value of type func(fn func())) as type func(func(), bool) in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / mobile_tests (1.23.x)

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / platform_tests (1.19.x, macos-latest)

cannot use func(fn func()) {…} (value of type func(fn func())) as type func(func(), bool) in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / platform_tests (1.23.x, macos-latest)

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / static_analysis

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / platform_tests (1.19.x, ubuntu-latest)

cannot use func(fn func()) {…} (value of type func(fn func())) as type func(func(), bool) in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / platform_tests (1.23.x, ubuntu-latest)

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / windows_tests (1.20.x)

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue

Check failure on line 20 in internal/app/lifecycle_test.go

GitHub Actions / windows_tests (1.22.x)

cannot use func(fn func()) {…} (value of type func(fn func())) as func(func(), bool) value in argument to life.RunEventQueue
fn()
})
life.QueueEvent(func() { called = true })
defer cancel()
<-ctx.Done()
fyne.CurrentApp().Driver().DoFromGoroutine(func() {

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / mobile_tests (1.19.x)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / mobile_tests (1.23.x)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / platform_tests (1.19.x, macos-latest)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / platform_tests (1.23.x, macos-latest)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / platform_tests (1.19.x, ubuntu-latest)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / platform_tests (1.23.x, ubuntu-latest)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / windows_tests (1.20.x)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine

Check failure on line 389 in internal/driver/mobile/canvas.go

GitHub Actions / windows_tests (1.22.x)

not enough arguments in call to fyne.CurrentApp().Driver().DoFromGoroutine
c.touchCancelLock.Lock()
touchCount := c.touchTapCount
touchLast := c.touchLastTapped