Creating and destroying a FireDomain leaks ~183 kB per domain as soon as a propagation layer is attached. Measured on dev at f00d722, looping construct → addPropagativeLayer("Rothermel") → delete:
| Domains |
RSS |
Growth |
| 1 |
15.7 MB |
— |
| 500 |
107 MB |
+91 MB |
| 1000 |
199 MB |
+183 MB |
| 2000 |
382 MB |
+367 MB |
Linear, no plateau. The same loop without addPropagativeLayer is flat (+4 kB over 2000 domains), so this is specific to the propagation-model path.
Two causes
The layer is never freed. src/FireDomain.cpp:335, in ~FireDomain:
if (propagativeLayer) {
//delete propagativeLayer;
propagativeLayer = nullptr;
}
Nothing owns the model. addPropagativeLayer calls propModelInstanciation, which returns a heap-allocated PropagationModel. That pointer reaches the static FireDomain::propModelsTable (50 slots, FireDomain.h:303; fluxModelsTable 500 slots, line 318) via DataBroker::registerPropagationModel. Neither ~FireDomain nor ~DataBroker ever clears those tables.
I have not attributed the full 183 kB to these two, but they are where ownership is provably absent.
Notes
Do this after #157 merges — freeing models while the double-delete is present turns a silent leak into a crash on every run. #156 and #157 fix the two bugs that would surface, and #156's suite already covers destruction of all 33 models.
The workload this hurts is the one being built: ensembles and long-running Python hosts create many domains per process.
Related latent bug. getFreePropModelIndex() (FireDomain.cpp:857) decrements a size_t with no lower bound, so a full table underflows and reads far out of bounds. getFreeFluxModelIndex bound-checks but then returns an occupied index, overwriting a live model. I could not reach it in a 60-domain loop — but it becomes reachable exactly when the static tables are made to fill up, which any fix here will touch.
Drafted by Claude Opus 5 from a codebase audit. Reviewed by a maintainer before filing.
EDIT: rewrote for human readability.
Creating and destroying a
FireDomainleaks ~183 kB per domain as soon as a propagation layer is attached. Measured ondevatf00d722, looping construct →addPropagativeLayer("Rothermel")→ delete:Linear, no plateau. The same loop without
addPropagativeLayeris flat (+4 kB over 2000 domains), so this is specific to the propagation-model path.Two causes
The layer is never freed.
src/FireDomain.cpp:335, in~FireDomain:Nothing owns the model.
addPropagativeLayercallspropModelInstanciation, which returns a heap-allocatedPropagationModel. That pointer reaches the staticFireDomain::propModelsTable(50 slots,FireDomain.h:303;fluxModelsTable500 slots, line 318) viaDataBroker::registerPropagationModel. Neither~FireDomainnor~DataBrokerever clears those tables.I have not attributed the full 183 kB to these two, but they are where ownership is provably absent.
Notes
Do this after #157 merges — freeing models while the double-delete is present turns a silent leak into a crash on every run. #156 and #157 fix the two bugs that would surface, and #156's suite already covers destruction of all 33 models.
The workload this hurts is the one being built: ensembles and long-running Python hosts create many domains per process.
Related latent bug.
getFreePropModelIndex()(FireDomain.cpp:857) decrements asize_twith no lower bound, so a full table underflows and reads far out of bounds.getFreeFluxModelIndexbound-checks but then returns an occupied index, overwriting a live model. I could not reach it in a 60-domain loop — but it becomes reachable exactly when the static tables are made to fill up, which any fix here will touch.Drafted by Claude Opus 5 from a codebase audit. Reviewed by a maintainer before filing.
EDIT: rewrote for human readability.