From 88ccf5dfb02a0037be13594433758f75a5c47148 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 14:45:09 +0000 Subject: [PATCH 1/9] Allow the thread migration to be disabled with tag `migrated_fynedo` or using new `Migrations` area in the FyneApp.toml --- app.go | 7 +++++-- app/meta.go | 16 ++++++++++------ app/meta_development.go | 1 + internal/async/goroutine.go | 4 ++-- internal/build/build.go | 14 +++++++++++++- internal/build/migrated_fynedo.go | 5 +++++ internal/build/migrated_notfynedo.go | 5 +++++ internal/metadata/data.go | 1 + 8 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 internal/build/migrated_fynedo.go create mode 100644 internal/build/migrated_notfynedo.go diff --git a/app.go b/app.go index ac1343935c..fd03849bb6 100644 --- a/app.go +++ b/app.go @@ -118,11 +118,14 @@ type AppMetadata struct { // Icon contains, if present, a resource of the icon that was bundled at build time. Icon Resource // Release if true this binary was build in release mode - // Since 2.3 + // Since: 2.3 Release bool // Custom contain the custom metadata defined either in FyneApp.toml or on the compile command line - // Since 2.3 + // Since: 2.3 Custom map[string]string + // Migrations allows an app to opt into features before they are standard + // Since: 2.6 + Migrations map[string]bool } // Lifecycle represents the various phases that an app can transition through. diff --git a/app/meta.go b/app/meta.go index 9693033845..214f13bbf1 100644 --- a/app/meta.go +++ b/app/meta.go @@ -5,12 +5,13 @@ import ( ) var meta = fyne.AppMetadata{ - ID: "", - Name: "", - Version: "0.0.1", - Build: 1, - Release: false, - Custom: map[string]string{}, + ID: "", + Name: "", + Version: "0.0.1", + Build: 1, + Release: false, + Custom: map[string]string{}, + Migrations: map[string]bool{}, } // SetMetadata overrides the packaged application metadata. @@ -21,6 +22,9 @@ func SetMetadata(m fyne.AppMetadata) { if meta.Custom == nil { meta.Custom = map[string]string{} } + if meta.Migrations == nil { + meta.Migrations = map[string]bool{} + } } func (a *fyneApp) Metadata() fyne.AppMetadata { diff --git a/app/meta_development.go b/app/meta_development.go index e91a3b3e33..0c29a7c08e 100644 --- a/app/meta_development.go +++ b/app/meta_development.go @@ -43,6 +43,7 @@ func checkLocalMetadata() { meta.Release = false meta.Custom = data.Development + meta.Migrations = data.Migrations } func getProjectPath() string { diff --git a/internal/async/goroutine.go b/internal/async/goroutine.go index 297fce5ba5..9c1ffe2bff 100644 --- a/internal/async/goroutine.go +++ b/internal/async/goroutine.go @@ -30,7 +30,7 @@ func IsMainGoroutine() bool { // // This will be removed later and should never be public func EnsureNotMain(fn func()) { - if build.DisableThreadChecks || !IsMainGoroutine() { + if (build.MigratedToFyneDo() || !build.HasHints) || !IsMainGoroutine() { fn() return } @@ -47,7 +47,7 @@ func EnsureNotMain(fn func()) { // // This will be removed later and should never be public func EnsureMain(fn func()) { - if build.DisableThreadChecks || IsMainGoroutine() { + if (build.MigratedToFyneDo() || !build.HasHints) || IsMainGoroutine() { fn() return } diff --git a/internal/build/build.go b/internal/build/build.go index 5013808f07..ee3f65033b 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1,4 +1,16 @@ // Package build contains information about they type of build currently running. package build -const DisableThreadChecks = false +import "fyne.io/fyne/v2" + +func MigratedToFyneDo() bool { + if DisableThreadChecks { + return true + } + + v, ok := fyne.CurrentApp().Metadata().Migrations["fyneDo"] + if ok { + return v + } + return false +} diff --git a/internal/build/migrated_fynedo.go b/internal/build/migrated_fynedo.go new file mode 100644 index 0000000000..b886d91dbe --- /dev/null +++ b/internal/build/migrated_fynedo.go @@ -0,0 +1,5 @@ +//go:build migrated_fynedo + +package build + +const DisableThreadChecks = true diff --git a/internal/build/migrated_notfynedo.go b/internal/build/migrated_notfynedo.go new file mode 100644 index 0000000000..3f0d89b1d7 --- /dev/null +++ b/internal/build/migrated_notfynedo.go @@ -0,0 +1,5 @@ +//go:build !migrated_fynedo + +package build + +const DisableThreadChecks = false diff --git a/internal/metadata/data.go b/internal/metadata/data.go index 99c9df3d37..30288fe924 100644 --- a/internal/metadata/data.go +++ b/internal/metadata/data.go @@ -9,6 +9,7 @@ type FyneApp struct { Source *AppSource `toml:",omitempty"` LinuxAndBSD *LinuxAndBSD `toml:",omitempty"` Languages []string `toml:",omitempty"` + Migrations map[string]bool `toml:",omitempty"` } // AppDetails describes the build information, this group may be OS or arch specific From 382bf7b936288946fd0db6d6f186cf9196b4b670 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 14:46:18 +0000 Subject: [PATCH 2/9] When building an app with fyneDo migration set apply the build tag --- cmd/fyne/internal/commands/build.go | 3 +++ cmd/fyne/internal/commands/data.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 7299ca865f..0555e1cb00 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -230,6 +230,9 @@ func (b *Builder) build() error { if b.release { tags = append(tags, "release") } + if ok, set := b.appData.Migrations["fyneDo"]; ok && set { + tags = append(tags, "migrated_fynedo") + } if len(tags) > 0 { args = append(args, "-tags", strings.Join(tags, ",")) } diff --git a/cmd/fyne/internal/commands/data.go b/cmd/fyne/internal/commands/data.go index 9052619664..7ada5fd3df 100644 --- a/cmd/fyne/internal/commands/data.go +++ b/cmd/fyne/internal/commands/data.go @@ -9,6 +9,7 @@ type appData struct { ResGoString string Release, rawIcon bool CustomMetadata map[string]string + Migrations map[string]bool VersionAtLeast2_3 bool } @@ -47,4 +48,5 @@ func (a *appData) mergeMetadata(data *metadata.FyneApp) { } else { a.appendCustomMetadata(data.Development) } + a.Migrations = data.Migrations } From 27c7b40427d23720347f42803693715603d4c04a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 14:47:12 +0000 Subject: [PATCH 3/9] Hello app is migrated (nothing required ;) ) --- cmd/hello/FyneApp.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/hello/FyneApp.toml b/cmd/hello/FyneApp.toml index d9266e7b17..44a86ddd82 100644 --- a/cmd/hello/FyneApp.toml +++ b/cmd/hello/FyneApp.toml @@ -4,3 +4,6 @@ ID = "io.fyne.hello" Version = "2.5.3" Build = 2 + +[Migrations] + fyneDo = true From d6e1787c20d74448c97faa71e32d2d7204d6289f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 14:48:16 +0000 Subject: [PATCH 4/9] Add `BaseWidget.Refresh` to the entry points that will check for main --- widget/widget.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/widget/widget.go b/widget/widget.go index fb38511750..986333f154 100644 --- a/widget/widget.go +++ b/widget/widget.go @@ -4,6 +4,7 @@ package widget // import "fyne.io/fyne/v2/widget" import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/internal/async" "fyne.io/fyne/v2/internal/cache" internalWidget "fyne.io/fyne/v2/internal/widget" "fyne.io/fyne/v2/theme" @@ -111,14 +112,16 @@ func (w *BaseWidget) Hide() { // Refresh causes this widget to be redrawn in its current state func (w *BaseWidget) Refresh() { - impl := w.super() - if impl == nil { - return - } + async.EnsureMain(func() { + impl := w.super() + if impl == nil { + return + } - w.themeCache = nil + w.themeCache = nil - cache.Renderer(impl).Refresh() + cache.Renderer(impl).Refresh() + }) } // Theme returns a cached Theme instance for this widget (or its extending widget). From 6f07422a1bfc38fd0d3a65138e6cad55ac7eb1e4 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 15:11:09 +0000 Subject: [PATCH 5/9] Fixup migration setting to be thread safe --- app/meta.go | 9 +++++++++ app/meta_development.go | 1 + internal/async/goroutine.go | 4 ++-- internal/build/build.go | 14 -------------- internal/build/migrated_fynedo.go | 2 +- internal/build/migrated_notfynedo.go | 2 +- 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/app/meta.go b/app/meta.go index 214f13bbf1..ecf9bc016c 100644 --- a/app/meta.go +++ b/app/meta.go @@ -2,6 +2,7 @@ package app import ( "fyne.io/fyne/v2" + "fyne.io/fyne/v2/internal/build" ) var meta = fyne.AppMetadata{ @@ -24,6 +25,8 @@ func SetMetadata(m fyne.AppMetadata) { } if meta.Migrations == nil { meta.Migrations = map[string]bool{} + } else { + setupMigrations(m.Migrations) } } @@ -34,3 +37,9 @@ func (a *fyneApp) Metadata() fyne.AppMetadata { return meta } + +func setupMigrations(data map[string]bool) { + if done, ok := data["fyneDo"]; ok && done { + build.DisableThreadChecks = true + } +} diff --git a/app/meta_development.go b/app/meta_development.go index 0c29a7c08e..f5562e2d34 100644 --- a/app/meta_development.go +++ b/app/meta_development.go @@ -44,6 +44,7 @@ func checkLocalMetadata() { meta.Release = false meta.Custom = data.Development meta.Migrations = data.Migrations + setupMigrations(data.Migrations) } func getProjectPath() string { diff --git a/internal/async/goroutine.go b/internal/async/goroutine.go index 9c1ffe2bff..c02b5d6962 100644 --- a/internal/async/goroutine.go +++ b/internal/async/goroutine.go @@ -30,7 +30,7 @@ func IsMainGoroutine() bool { // // This will be removed later and should never be public func EnsureNotMain(fn func()) { - if (build.MigratedToFyneDo() || !build.HasHints) || !IsMainGoroutine() { + if (build.DisableThreadChecks || !build.HasHints) || !IsMainGoroutine() { fn() return } @@ -47,7 +47,7 @@ func EnsureNotMain(fn func()) { // // This will be removed later and should never be public func EnsureMain(fn func()) { - if (build.MigratedToFyneDo() || !build.HasHints) || IsMainGoroutine() { + if (build.DisableThreadChecks || !build.HasHints) || IsMainGoroutine() { fn() return } diff --git a/internal/build/build.go b/internal/build/build.go index ee3f65033b..7db2881d5e 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1,16 +1,2 @@ // Package build contains information about they type of build currently running. package build - -import "fyne.io/fyne/v2" - -func MigratedToFyneDo() bool { - if DisableThreadChecks { - return true - } - - v, ok := fyne.CurrentApp().Metadata().Migrations["fyneDo"] - if ok { - return v - } - return false -} diff --git a/internal/build/migrated_fynedo.go b/internal/build/migrated_fynedo.go index b886d91dbe..d05d9de457 100644 --- a/internal/build/migrated_fynedo.go +++ b/internal/build/migrated_fynedo.go @@ -2,4 +2,4 @@ package build -const DisableThreadChecks = true +var DisableThreadChecks = true diff --git a/internal/build/migrated_notfynedo.go b/internal/build/migrated_notfynedo.go index 3f0d89b1d7..5e3d2555c0 100644 --- a/internal/build/migrated_notfynedo.go +++ b/internal/build/migrated_notfynedo.go @@ -2,4 +2,4 @@ package build -const DisableThreadChecks = false +var DisableThreadChecks = false From 0c2c0ef13bfd107498a9e19c0a4449db7a518bad Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 15:15:44 +0000 Subject: [PATCH 6/9] Documentation for the fields --- internal/build/migrated_fynedo.go | 1 + internal/build/migrated_notfynedo.go | 1 + 2 files changed, 2 insertions(+) diff --git a/internal/build/migrated_fynedo.go b/internal/build/migrated_fynedo.go index d05d9de457..d2129bbeab 100644 --- a/internal/build/migrated_fynedo.go +++ b/internal/build/migrated_fynedo.go @@ -2,4 +2,5 @@ package build +// DisableThreadChecks disables the thread safety checks for performance. var DisableThreadChecks = true diff --git a/internal/build/migrated_notfynedo.go b/internal/build/migrated_notfynedo.go index 5e3d2555c0..bfcc1b8b2a 100644 --- a/internal/build/migrated_notfynedo.go +++ b/internal/build/migrated_notfynedo.go @@ -2,4 +2,5 @@ package build +// DisableThreadChecks set to false enables the thread safety checks for logging of incorrect usage. var DisableThreadChecks = false From 8f4525d997bc3a05bf7caa452b4eeff9ed7a1c2e Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 15:17:51 +0000 Subject: [PATCH 7/9] Remove parenths --- internal/async/goroutine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/async/goroutine.go b/internal/async/goroutine.go index c02b5d6962..459120f90e 100644 --- a/internal/async/goroutine.go +++ b/internal/async/goroutine.go @@ -30,7 +30,7 @@ func IsMainGoroutine() bool { // // This will be removed later and should never be public func EnsureNotMain(fn func()) { - if (build.DisableThreadChecks || !build.HasHints) || !IsMainGoroutine() { + if build.DisableThreadChecks || !build.HasHints || !IsMainGoroutine() { fn() return } @@ -47,7 +47,7 @@ func EnsureNotMain(fn func()) { // // This will be removed later and should never be public func EnsureMain(fn func()) { - if (build.DisableThreadChecks || !build.HasHints) || IsMainGoroutine() { + if build.DisableThreadChecks || !build.HasHints || IsMainGoroutine() { fn() return } From 3e7e92d31d276092823691f09c6a987a18805aa8 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 15:21:34 +0000 Subject: [PATCH 8/9] Revert "Fixup migration setting to be thread safe" This reverts commit 6f07422a1bfc38fd0d3a65138e6cad55ac7eb1e4. --- app/meta.go | 9 --------- app/meta_development.go | 1 - internal/async/goroutine.go | 4 ++-- internal/build/build.go | 14 ++++++++++++++ internal/build/migrated_fynedo.go | 2 +- internal/build/migrated_notfynedo.go | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/meta.go b/app/meta.go index ecf9bc016c..214f13bbf1 100644 --- a/app/meta.go +++ b/app/meta.go @@ -2,7 +2,6 @@ package app import ( "fyne.io/fyne/v2" - "fyne.io/fyne/v2/internal/build" ) var meta = fyne.AppMetadata{ @@ -25,8 +24,6 @@ func SetMetadata(m fyne.AppMetadata) { } if meta.Migrations == nil { meta.Migrations = map[string]bool{} - } else { - setupMigrations(m.Migrations) } } @@ -37,9 +34,3 @@ func (a *fyneApp) Metadata() fyne.AppMetadata { return meta } - -func setupMigrations(data map[string]bool) { - if done, ok := data["fyneDo"]; ok && done { - build.DisableThreadChecks = true - } -} diff --git a/app/meta_development.go b/app/meta_development.go index f5562e2d34..0c29a7c08e 100644 --- a/app/meta_development.go +++ b/app/meta_development.go @@ -44,7 +44,6 @@ func checkLocalMetadata() { meta.Release = false meta.Custom = data.Development meta.Migrations = data.Migrations - setupMigrations(data.Migrations) } func getProjectPath() string { diff --git a/internal/async/goroutine.go b/internal/async/goroutine.go index 459120f90e..8f2b412905 100644 --- a/internal/async/goroutine.go +++ b/internal/async/goroutine.go @@ -30,7 +30,7 @@ func IsMainGoroutine() bool { // // This will be removed later and should never be public func EnsureNotMain(fn func()) { - if build.DisableThreadChecks || !build.HasHints || !IsMainGoroutine() { + if build.MigratedToFyneDo() || !build.HasHints || !IsMainGoroutine() { fn() return } @@ -47,7 +47,7 @@ func EnsureNotMain(fn func()) { // // This will be removed later and should never be public func EnsureMain(fn func()) { - if build.DisableThreadChecks || !build.HasHints || IsMainGoroutine() { + if build.MigratedToFyneDo() || !build.HasHints || IsMainGoroutine() { fn() return } diff --git a/internal/build/build.go b/internal/build/build.go index 7db2881d5e..ee3f65033b 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1,2 +1,16 @@ // Package build contains information about they type of build currently running. package build + +import "fyne.io/fyne/v2" + +func MigratedToFyneDo() bool { + if DisableThreadChecks { + return true + } + + v, ok := fyne.CurrentApp().Metadata().Migrations["fyneDo"] + if ok { + return v + } + return false +} diff --git a/internal/build/migrated_fynedo.go b/internal/build/migrated_fynedo.go index d2129bbeab..089f766dce 100644 --- a/internal/build/migrated_fynedo.go +++ b/internal/build/migrated_fynedo.go @@ -3,4 +3,4 @@ package build // DisableThreadChecks disables the thread safety checks for performance. -var DisableThreadChecks = true +const DisableThreadChecks = true diff --git a/internal/build/migrated_notfynedo.go b/internal/build/migrated_notfynedo.go index bfcc1b8b2a..a9fbdd5479 100644 --- a/internal/build/migrated_notfynedo.go +++ b/internal/build/migrated_notfynedo.go @@ -3,4 +3,4 @@ package build // DisableThreadChecks set to false enables the thread safety checks for logging of incorrect usage. -var DisableThreadChecks = false +const DisableThreadChecks = false From dfad1bf515fd7f45605f81401bf07478f712062d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 17 Jan 2025 15:28:49 +0000 Subject: [PATCH 9/9] use sync.Once instead to maintain the const optimisations and avoid races --- internal/build/build.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index ee3f65033b..8567284fcd 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1,16 +1,29 @@ // Package build contains information about they type of build currently running. package build -import "fyne.io/fyne/v2" +import ( + "sync" + + "fyne.io/fyne/v2" +) + +var ( + migrateCheck sync.Once + + migratedFyneDo bool +) func MigratedToFyneDo() bool { if DisableThreadChecks { return true } - v, ok := fyne.CurrentApp().Metadata().Migrations["fyneDo"] - if ok { - return v - } - return false + migrateCheck.Do(func() { + v, ok := fyne.CurrentApp().Metadata().Migrations["fyneDo"] + if ok { + migratedFyneDo = v + } + }) + + return migratedFyneDo }