fix: protect the dirty flag with an atomic.Bool
SetDirty runs on a Wails call goroutine while the GTK close handler reads the flag, with no synchronisation: an edit landing while a save finished could make the close confirmation unreliable. The spellMu comment already documents the goroutine-per-call convention; the flag that guards against losing work deserves the same care.
This commit is contained in:
1 parent
643adffb12
commit
c0a52a9040
2 files changed
+13
-4
No files matched your search
@@ -7,6 +7,11 @@ version is in the `VERSION` file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The unsaved-changes marker is now updated atomically: an edit landing while a save
|
||||||
|
finished could make the close confirmation unreliable.
|
||||||
|
|
||||||
## [1.4] - 2026-10-15
|
## [1.4] - 2026-10-15
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
@@ -24,8 +25,11 @@ import (
|
|||||||
|
|
||||||
// App is bound to the frontend: every exported method is callable from JS.
|
// App is bound to the frontend: every exported method is callable from JS.
|
||||||
type App struct {
|
type App struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dirty bool
|
// dirty decides whether closing the window asks for confirmation. It is
|
||||||
|
// written from Wails call goroutines (SetDirty) and read by the GTK
|
||||||
|
// close handler: atomic, like spellMu protects the checker.
|
||||||
|
dirty atomic.Bool
|
||||||
initialPath string
|
initialPath string
|
||||||
|
|
||||||
// Wails runs each call on its own goroutine: spelling calls share one
|
// Wails runs each call on its own goroutine: spelling calls share one
|
||||||
@@ -74,7 +78,7 @@ func (a *App) startup(ctx context.Context) {
|
|||||||
// beforeClose keeps the window open while there are unsaved changes and
|
// beforeClose keeps the window open while there are unsaved changes and
|
||||||
// lets the frontend ask; it quits again once the user has decided.
|
// lets the frontend ask; it quits again once the user has decided.
|
||||||
func (a *App) beforeClose(ctx context.Context) bool {
|
func (a *App) beforeClose(ctx context.Context) bool {
|
||||||
if !a.dirty {
|
if !a.dirty.Load() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
runtime.EventsEmit(a.ctx, "confirm-close")
|
runtime.EventsEmit(a.ctx, "confirm-close")
|
||||||
@@ -201,7 +205,7 @@ func (a *App) RevisionSet(n int) screenplay.Revision {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SetDirty(dirty bool) { a.dirty = dirty }
|
func (a *App) SetDirty(dirty bool) { a.dirty.Store(dirty) }
|
||||||
|
|
||||||
// dataDir is the XDG data directory (~/.local/share).
|
// dataDir is the XDG data directory (~/.local/share).
|
||||||
func dataDir() string {
|
func dataDir() string {
|
||||||
|
|||||||
Reference in new issue
Block a user