Skip to content

Clone the registry GTouchable per step to stop cross-event state leak#165

Merged
maureeungaro merged 1 commit into
gemc:mainfrom
zhaozhiwen:fix/113-touchable-state-leak
Jun 13, 2026
Merged

Clone the registry GTouchable per step to stop cross-event state leak#165
maureeungaro merged 1 commit into
gemc:mainfrom
zhaozhiwen:fix/113-touchable-state-leak

Conversation

@zhaozhiwen

Copy link
Copy Markdown
Collaborator

getGTouchable returned the persistent shared_ptr<GTouchable> stored in gTouchableMap, and ProcessHits/processTouchable mutate it each step (trackId, pid, stepTimeAtElectronicsIndex). Initialize() clears the per-event touchableVector but never resets those mutations, so at the start of each event the registry object still holds the previous event's time-cell index. processTouchableImpl branches on that index, so the first step of a new event can be misrouted (a spurious clone / wrong time cell), and stale trackId/pid carry over.

Return a per-step copy so the registry entry stays pristine. Each step then starts from the clean UNSET identity, and within-event grouping still works because the hit collection / touchableVector group by operator== over field values (freshly assigned per step), not by object identity. gsd.h documents instances as thread-local with a per-worker map, so the clone introduces no cross-thread sharing.

Validation: ran the cherenkov example (gPhotonDetector SD, 20 events, fixed -seed) before and after — the hit physics is byte-identical (only the wall-clock timestamp column differs), i.e. no regression, while the fix removes the cross-event index carryover that affects readout SDs (Geant4 11.4.1 dev container).

Note: this is the issue's primary (clone) fix. For readout-type SDs it changes behavior by design — it stops the cross-event bleed and the spurious double-cell contribution from processTouchableImpl's clone-on-change branch; each step now routes to exactly one time cell via operator==.

Fixes #113

getGTouchable returned the persistent shared_ptr<GTouchable> stored in
gTouchableMap, and ProcessHits/processTouchable mutate it each step
(trackId, pid, stepTimeAtElectronicsIndex). Initialize() clears the
per-event touchableVector but never resets those mutations, so at the
start of each event the registry object still holds the previous event's
time-cell index. processTouchableImpl branches on that index, so the
first step of a new event can be misrouted (a spurious clone / wrong time
cell), and stale trackId/pid carry over.

Return a per-step copy so the registry entry stays pristine. Each step
then starts from the clean UNSET identity, and within-event grouping
still works because the hit collection / touchableVector group by
operator== over field values (which are freshly assigned per step), not
by object identity. gsd.h documents instances as thread-local with a
per-worker map, so the clone introduces no cross-thread sharing.

Verified: cherenkov example (gPhotonDetector SD, 20 events, fixed seed)
produces byte-identical hit physics before and after (no regression); the
fix removes the cross-event index carryover that affects readout SDs
(Geant4 11.4.1 dev container).

Fixes gemc#113

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@maureeungaro maureeungaro merged commit ca9c451 into gemc:main Jun 13, 2026
30 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.

[High] ProcessHits mutates the persistent map GTouchable, leaking state across events

2 participants