Skip to content

state: preserve sync.Once completion across transfers - #31

Merged
MeteorsLiu merged 1 commit into
xgo-dev:mainfrom
MeteorsLiu:codex/state-sync-once
Sep 24, 2026
Merged

MeteorsLiu merged 1 commit into
xgo-dev:mainfrom
MeteorsLiu:codex/state-sync-once

Conversation

@MeteorsLiu

Copy link
Copy Markdown
Collaborator

No description provided.

@codecov

codecov Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: sync.Once support in state serialization

The change adds a single, well-targeted arm to syncFields so sync.Once serializes only its done flag, while decodeStruct's obj.SetZero() (decode.go:443) zeros the surrounding struct — including the embedded Mutex — before restoring done. This yields a fresh, unlocked mutex on load, which is the correct behavior. I confirmed against the go1.26.6 sync.Once source that done is set via defer even when the action panics, so the test's want = 0 expectation for the panic status is right.

Strengths

  • Clean reuse of existing infrastructure: the nested done atomic.Bool re-enters syncFields, and read-only-flag handling in encode/decode makes unexported-field access safe with no extra code.
  • The expanded TestSyncOnceZero is thorough — the pre-locked-mutex TryLock assertion validates mutex reset, the 8-goroutine fan-out confirms exactly-once semantics survive restore, and the writeback subtest verifies pointer-alias preservation, guest/host isolation, and correctness across two full save/load cycles.

Notes

  • No blocking issues. One robustness note is left inline regarding version-gating of the field access.
  • Nit: the doc comment's "fresh mutex on load" describes an effect produced generically by SetZero() for all syncFields types, not Once-specific logic — optional wording clarification.

Tests could not be executed in this environment due to a pre-existing ixgo toolchain linking issue (invalid reference to reflect.ptrMap), unrelated to this PR; review is static.

Comment thread internal/state/sync.go
Comment on lines +24 to +25
case reflect.TypeFor[sync.Once]():
return []reflect.Value{obj.FieldByName("done")}, true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Once/Pool/Cond field access is not version-gated

The sync.Once case uses obj.FieldByName("done") with no field-presence or version guard, and the Once path returns before the runtime.Version() != "go1.26.6" assertion at line 42 (that check only guards the sync/atomic paths). The pre-existing Pool.New/Cond.L arms share this gap. If a future Go release renames or restructures these fields, FieldByName returns an invalid reflect.Value and encode/decode panics at first snapshot of such a type rather than failing loudly at startup. Since the codebase already hard-pins go1.26.6 for the atomic paths, consider extending an equivalent explicit version/field-presence assertion to the Once (and Pool/Cond) cases so a Go upgrade fails early with a clear message. Robustness/portability only — not exploitable and not a correctness defect at the pinned version.

@MeteorsLiu
MeteorsLiu merged commit a531c10 into xgo-dev:main Sep 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant