aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/plan.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 14:19:23 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 14:19:23 +0200
commit40dbf18893c33bc40c8617d0f3e8de16ce2947e2 (patch)
tree01f9d77818c2dbfce1684895f562ae3dc865d44c /gui/internal/model/plan.go
parentb66850cc8c584cc00bcd796eb3aa238bcf87394a (diff)
downloadkrino-40dbf18893c33bc40c8617d0f3e8de16ce2947e2.tar.gz
krino-40dbf18893c33bc40c8617d0f3e8de16ce2947e2.zip
the applying flag is atomic; its test waits instead of polling
The race detector found my own new flag: Apply writes it on a worker and Close reads it from the main loop. The test was polling a plain field too, where the real window hears about the apply on the main loop, which orders the writes.
Diffstat (limited to 'gui/internal/model/plan.go')
-rw-r--r--gui/internal/model/plan.go13
1 files changed, 8 insertions, 5 deletions
diff --git a/gui/internal/model/plan.go b/gui/internal/model/plan.go
index 31a783b..31c12dd 100644
--- a/gui/internal/model/plan.go
+++ b/gui/internal/model/plan.go
@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"sort"
+ "sync/atomic"
"time"
"git.labunix.xyz/krino/internal/engine"
@@ -53,14 +54,16 @@ type PlanTab struct {
dp *engine.DirPlan
lock *lock.Lock
- // applying is set while Apply is in flight. Close must refuse then:
+ // applying is set while Apply is in flight, and read from the main loop
+ // while the apply runs on a worker, so it is atomic. Close must refuse
+ // while it is set:
// releasing the lock and closing the log under a running apply moves
// files the log never records, so undo cannot see them, and unlocks a
// directory krino is still working in. The window can reach Close from
// several places while an apply runs - saving rules or settings, adding
// a directory, closing the window - so the refusal lives here rather
// than in whichever of them remembers.
- applying bool
+ applying atomic.Bool
// testBeforeApply, when set, is called just before the engine's Apply
// begins, so a test can act while the apply is in flight.
@@ -113,7 +116,7 @@ func (t *PlanTab) Run() string { return t.sess.Run() }
// Close releases the directory's lock and ends the run, which Apply also
// does once the plan is history. Closing twice is not an error.
func (t *PlanTab) Close() error {
- if t.applying {
+ if t.applying.Load() {
return ErrApplying
}
if t.lock == nil {
@@ -341,12 +344,12 @@ func (t *PlanTab) Apply(ctx context.Context) (*engine.ApplyResult, error) {
approved[r.Rel] = true
}
}
- t.applying = true
+ t.applying.Store(true)
if t.testBeforeApply != nil {
t.testBeforeApply()
}
res, err := t.sess.Apply(ctx, t.dp, approved)
- t.applying = false
+ t.applying.Store(false)
t.Applied = true
// An applied plan is history: the run is over and the directory free
// again, without closing the window (GUI design ยง3).