Skip to content

Commit 0f2922a

Browse files
zhaozhiwenclaude
andcommitted
Clone the registry GTouchable per step to stop cross-event state leak
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 #113 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 48f83e5 commit 0f2922a

1 file changed

Lines changed: 3 additions & 1 deletion

File tree

gemc/gsd/gsd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ class GSensitiveDetector : public GBase<GSensitiveDetector>, public G4VSensitive
195195
std::string vname = thisStep->GetPreStepPoint()->GetTouchable()->GetVolume()->GetName();
196196

197197
auto it = gTouchableMap.find(vname);
198-
if (it != gTouchableMap.end()) { return it->second; }
198+
// Return a per-step copy: ProcessHits and processTouchable mutate trackId, pid and the
199+
// time-cell index, so the registry entry must stay pristine across steps and events.
200+
if (it != gTouchableMap.end()) { return std::make_shared<GTouchable>(*it->second); }
199201
// If not found, log an error. The calling code assumes a valid pointer.
200202
log->error(ERR_DYNAMICPLUGINNOTFOUND, "GTouchable for volume " + vname + " not found in gTouchableMap");
201203
}

0 commit comments

Comments
 (0)