Fix a memory leak in map::generate reported by LSAN in the CI build #36410
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
SUMMARY: Bugfixes "Fix a memory leak during map generation"
Purpose of change
Recent CI builds consistently reported a memory leak in
map::generate
, for example in this and this report. After extensive test and analysis I finally found the reason. Basically it goes like this:In the following lines in
map::generate
, a map extra has a chance to be applied (MapExtras::apply_function
) when generating an overmap chunk (2x2 submaps). When this happens, the newsubmap
s constructed by themap
has not yet been added to the map buffer, so if themap
is atinymap
, and the overmap chunk was not previous generated, the submaps in the map buffer is stillnullptr
at this time.Cataclysm-DDA/src/mapgen.cpp
Lines 145 to 154 in 5f963d1
However, in
MapExtras::apply_function
,run_mapgen_update_func
is called, which in turn callsupdate_mapgen_function_json::update_map( const tripoint &, const point &, mission *, bool )
, in which atinymap
is further constructed and loaded (map::load
). Since thesubmap
s generated before are yet to be added to the map buffer,map::loadn
determines (wrongly) that thesubmap
s are not yet generated, and callsmap::generate
again, which constructs yet another set of newsubmap
s and save them to the map buffer (or, if map extras are again randomly applied in this call tomap::generate
, the nested calls will continue until no map extra is applied in a final call tomap::generate
). When the final nested call tomap::generate
finishes, the outer calls tomap::generate
fail to add their own constructedsubmap
s to the map buffer (sincesubmap
s have already been added to the corresponding submap locations in the final call tomap::generate
), but do not deallocate them, causing the memory leaks reported by LSAN.Cataclysm-DDA/src/map_extras.cpp
Lines 2749 to 2752 in 5f963d1
Cataclysm-DDA/src/mapgen.cpp
Lines 7746 to 7751 in 5f963d1
This also explains why these memory leaks have been intermittent among recent CI builds, since the memory leaks only happen if a map extra is applied in
map::generate
.Describe the solution
update_mapgen_function_json::update_map( const tripoint &, const point &, mission *, bool )
to its called functionupdate_mapgen_function_json::update_map( mapgendata &, const point &, bool )
, and removed the call tomap::save
. Whatmap::save
really does is only adding thesubmap
s to the map buffer, which is already done inmap::loadn
, so the call tomap::save
was redundant. This means the behavior ofupdate_mapgen_function_json::update_map( const tripoint &, const point &, mission *, bool )
has not been changed. On the other hand,update_mapgen_function_json::update_map( mapgendata &, const point &, bool )
now rotates the map passed to it, but it was previously only called inget_changed_ids_from_update
, and rotating the map in that case is the desired behavior.run_mapgen_update_func
function that acceptsmapgendata
as its parameter. It has the same behavior as the oldrun_mapgen_update_func
, except that it updates themap
reference inmapgendata
instead of a newly loadedtinymap
.MapExtras::apply_function
to call the newrun_mapgen_update_func
function instead, so the map extra is applied on top of the exact mapMapExtras::apply_function
is invoked with, instead of a newly loadedtinymap
. This ensures that the newsubmap
s constructed inmap::generate
is always visible toupdate_mapgen_function_json::update_map
, somap::load
doesn't need to be called and wrongly assume that the submaps still needs generation, thus avoiding the nested calls tomap::generate
and the memory leak.Describe alternatives you've considered
The whole mapgen code needs some revision, but I'm not trying to delve too deep into that area now.
Testing
Tested in game and map generation was working as before. However I don't have LSAN in my environment, so I can't test locally whether the memory leak has been fixed. However my analysis strongly suggests this is the correct fix.