Skip to content

Remove the delete-while-iterating hazard in delta ensemble case removal #14517

Description

@magnesj

#14424 stopped the crash in #14423 by rewriting the caller's case list with the survivors. That fixes one call site, but the hazard is general: RimDeltaSummaryEnsemble deleted its derived cases inline, from inside a loop the caller was still walking. Any caller holding a case list across removeCase() had the same bug.

Fixed structurally on lifetime-delta-ensemble-batch. The band-aid is removed as part of it.

Detach never destroys. rebuildDerivedCases() replaces the m_inUse pooling. It diffs the derived cases against the desired source pairs, keyed on the (case1, case2) pointer pair, reuses matches, creates what is missing, and returns the surplus instead of deleting it. Keying on the pointer pair makes the rebuild idempotent, including right after project load.

Destruction happens at a batch flush. RimSummaryCaseUpdateBatch is a plain scope object, ambient for its scope. Detached cases are destroyed when the outermost scope ends, after the dirty delta ensembles have regenerated in dependency order. Orphans are held as caf::PdmPointer, so a case the caller destroyed itself is skipped rather than destroyed twice. With no batch active both contribution points fall back to immediate execution, so unconverted call sites keep working. RicCloseSummaryCaseFeature::deleteSummaryCases opens the outermost batch, because it deletes the case list after removeCases returns.

Dependency traversal by PDM back-reference. RimSummaryEnsembleTools gains dependentDeltaEnsembles(), deltaEnsemblesInUpdateOrder() and wouldCreateDependencyCycle() — deduplicated, iterative, topologically ordered, back edges logged rather than traversed. Replaces findReferringEnsembles(), which recursed with no visited set and ran forever on a dependency cycle.

Verification

1033 unit tests pass. RemoveCases_NoDanglingInCallerVector asserts that every entry the caller handed over is alive inside the batch scope and destroyed exactly once after it. Removing just the batch line from that test reproduces the original signature: cases.size() 6 against aliveCount() 4, followed by SEH exception with code 0xc0000005.

Known follow-up

A dependency cycle stack-overflows in auto-name generation, independently of this fix: RimSummaryEnsemble::updateName ends in caseNameChanged.send(), which reaches back into updateSummaryEnsembleNames(). It terminates only because names converge, and auto-generated names never converge when A's name derives from B's and B's from A's. Cycle prevention in setEnsemble1()/setEnsemble2() is the fix.

Related: #14423, #14424, #14491, #14493. Branch and staged plan: magnesj#1042.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugInReleaseBug in an official release

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions