Skip to content

Commit b67c9f4

Browse files
Fix memory leak from repeated ProjDataInfo::clone() calls (UCL#1673)
* Fix memory leak in ScatterSimulation from repeated ProjDataInfo::clone() calls * Small edit to add a new method to ScatterSimulation class to set the template projection data info using a ProjDataInfo object directly, instead of only through a shared pointer. This new method creates a shared pointer from the given ProjDataInfo object and then calls the existing method that takes a shared pointer. * minor clean-up of test_blocks_on_cylindrical. Co-authored-by: Kris Thielemans <KrisThielemans@users.noreply.github.com>
1 parent 25b3656 commit b67c9f4

File tree

8 files changed

+48
-30
lines changed

8 files changed

+48
-30
lines changed

documentation/release_6.4.htm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ <h3>Bug fixes</h3>
7373
segments had problems, see <a href=https://github.com/UCL/STIR/issues/1537># 1537</a>.<br>
7474
<a href=https://github.com/UCL/STIR/pull/1675># 1675</a>. Extra tests were introduced in <a href=https://github.com/UCL/STIR/pull/1674># 1674</a>.
7575
</li>
76+
<li>
77+
Fixed a small memory leak in scatter estimation caused by multiple calls to <code>ProjDataInfo::clone()</code> combined with <code>dynamic_cast</code>.
78+
The object is now cloned once and ownership is transferred safely after type checking.<br>
79+
<a href=https://github.com/UCL/STIR/pull/1673>PR #1673</a>
80+
</li>
81+
<br>
7682
</ul>
7783

7884
<h3>Build system</h3>

src/include/stir/scatter/ScatterSimulation.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ class ScatterSimulation : public RegisteredObject<ScatterSimulation>
124124

125125
void set_template_proj_data_info(const std::string&);
126126

127-
void set_template_proj_data_info(const ProjDataInfo&);
127+
void set_template_proj_data_info(const ProjDataInfo& arg);
128+
129+
void set_template_proj_data_info(shared_ptr<const ProjDataInfo> arg);
128130

129131
void set_activity_image_sptr(const shared_ptr<const DiscretisedDensity<3, float>>);
130132

@@ -334,7 +336,7 @@ class ScatterSimulation : public RegisteredObject<ScatterSimulation>
334336

335337
std::string template_proj_data_filename;
336338

337-
shared_ptr<ProjDataInfo> proj_data_info_sptr;
339+
shared_ptr<const ProjDataInfo> proj_data_info_sptr;
338340
//! \details Exam info extracted from the scanner template
339341
shared_ptr<ExamInfo> template_exam_info_sptr;
340342

src/recon_test/test_blocks_on_cylindrical_projectors.cxx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ BlocksTests::run_symmetry_test(ForwardProjectorByBin& forw_projector1, ForwardPr
195195
shared_ptr<DiscretisedDensity<3, float>> image1_sptr(image.clone());
196196
write_to_file("image_for", *image1_sptr);
197197

198-
image = *image.get_empty_copy();
198+
image.fill(0);
199199
for (int i = 15; i < 360; i += 30)
200200
{
201201
theta2 = i * _PI / 180;
@@ -307,7 +307,8 @@ BlocksTests::run_plane_symmetry_test(ForwardProjectorByBin& forw_projector1, For
307307

308308
// rotate by 30 degrees
309309
phi2 = 30 * _PI / 180;
310-
VoxelsOnCartesianGrid<float> image2 = *image.get_empty_copy();
310+
VoxelsOnCartesianGrid<float> image2(image);
311+
image2.fill(0);
311312
const Array<2, float> direction2 = make_array(make_1d_array(1.F, 0.F, 0.F),
312313
make_1d_array(0.F, cos(float(_PI) - phi2), sin(float(_PI) - phi2)),
313314
make_1d_array(0.F, -sin(float(_PI) - phi2), cos(float(_PI) - phi2)));
@@ -591,7 +592,7 @@ BlocksTests::run_map_orientation_test(ForwardProjectorByBin& forw_projector1, Fo
591592
shared_ptr<DiscretisedDensity<3, float>> image1_sptr(image.clone());
592593
write_to_file("image_to_fwp", *image1_sptr);
593594

594-
image = *image.get_empty_copy();
595+
image.fill(0);
595596

596597
shared_ptr<const DetectorCoordinateMap> map_sptr;
597598
auto scannerBlocks_sptr = std::make_shared<Scanner>(Scanner::SAFIRDualRingPrototype);
@@ -700,7 +701,7 @@ BlocksTests::run_projection_test(ForwardProjectorByBin& forw_projector1, Forward
700701
shared_ptr<DiscretisedDensity<3, float>> image1_sptr(image.clone());
701702
write_to_file("image_with_voxel_at_30_0", *image1_sptr);
702703

703-
image = *image.get_empty_copy();
704+
image.fill(0);
704705
image[(image.get_min_index() + image.get_max_index()) / 2 * grid_spacing.z()][0][-25] = 1;
705706

706707
shared_ptr<DiscretisedDensity<3, float>> image2_sptr(image.clone());

src/scatter_buildblock/ScatterEstimation.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,12 +610,12 @@ ScatterEstimation::set_up()
610610
"discarded)");
611611
if (run_in_2d_projdata)
612612
{
613-
this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_2d_sptr->get_proj_data_info_sptr());
613+
this->scatter_simulation_sptr->set_template_proj_data_info(this->input_projdata_2d_sptr->get_proj_data_info_sptr());
614614
this->scatter_simulation_sptr->set_exam_info(this->input_projdata_2d_sptr->get_exam_info());
615615
}
616616
else
617617
{
618-
this->scatter_simulation_sptr->set_template_proj_data_info(*this->input_projdata_sptr->get_proj_data_info_sptr());
618+
this->scatter_simulation_sptr->set_template_proj_data_info(this->input_projdata_sptr->get_proj_data_info_sptr());
619619
this->scatter_simulation_sptr->set_exam_info(this->input_projdata_sptr->get_exam_info());
620620
}
621621
}

src/scatter_buildblock/ScatterSimulation.cxx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,14 @@ ScatterSimulation::set_up()
352352
{
353353
CartesianCoordinate3D<float> detector_coord_A, detector_coord_B;
354354
// check above statement
355-
if (dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
355+
if (dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
356356
{
357-
auto ptr = dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
357+
const auto* ptr = dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
358358
ptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, Bin(0, 0, 0, 0));
359359
}
360360
else
361361
{
362-
auto ptr = dynamic_cast<ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
362+
const auto* ptr = dynamic_cast<const ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
363363
ptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, Bin(0, 0, 0, 0));
364364
}
365365

@@ -374,14 +374,14 @@ ScatterSimulation::set_up()
374374
assert(fabs(m_last + m_first) < m_last * 10E-4);
375375
}
376376
#endif
377-
if (dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
377+
if (dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
378378
{
379379
this->shift_detector_coordinates_to_origin
380380
= CartesianCoordinate3D<float>(this->proj_data_info_sptr->get_m(Bin(0, 0, 0, 0)), 0, 0);
381381
}
382382
else
383383
{
384-
if (dynamic_cast<ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
384+
if (dynamic_cast<const ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
385385
{
386386
// align BlocksOnCylindrical scanner ring 0 to z=0.
387387
this->shift_detector_coordinates_to_origin
@@ -736,22 +736,31 @@ ScatterSimulation::set_template_proj_data_info(const std::string& filename)
736736

737737
this->set_exam_info(template_proj_data_sptr->get_exam_info());
738738

739-
this->set_template_proj_data_info(*template_proj_data_sptr->get_proj_data_info_sptr());
739+
this->set_template_proj_data_info(template_proj_data_sptr->get_proj_data_info_sptr());
740740
}
741741

742742
void
743743
ScatterSimulation::set_template_proj_data_info(const ProjDataInfo& arg)
744744
{
745-
this->_already_set_up = false;
746-
this->proj_data_info_sptr.reset(dynamic_cast<ProjDataInfoBlocksOnCylindricalNoArcCorr*>(arg.clone()));
745+
shared_ptr<const ProjDataInfo> sptr(arg.create_shared_clone());
746+
this->set_template_proj_data_info(sptr);
747+
}
747748

748-
if (is_null_ptr(this->proj_data_info_sptr))
749+
void
750+
ScatterSimulation::set_template_proj_data_info(shared_ptr<const ProjDataInfo> arg)
751+
{
752+
this->_already_set_up = false;
753+
if (auto p = std::dynamic_pointer_cast<const ProjDataInfoBlocksOnCylindricalNoArcCorr>(arg))
749754
{
750-
this->proj_data_info_sptr.reset(dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(arg.clone()));
751-
if (is_null_ptr(this->proj_data_info_sptr))
752-
{
753-
error("ScatterSimulation: Can only handle non-arccorrected data");
754-
}
755+
this->proj_data_info_sptr = p;
756+
}
757+
else if (auto q = std::dynamic_pointer_cast<const ProjDataInfoCylindricalNoArcCorr>(arg))
758+
{
759+
this->proj_data_info_sptr = q;
760+
}
761+
else
762+
{
763+
error("ScatterSimulation: Can only handle non-arccorrected data");
755764
}
756765

757766
// find final size of detection_points_vector
@@ -944,7 +953,7 @@ ScatterSimulation::downsample_scanner(int new_num_rings, int new_num_dets)
944953
false));
945954

946955
info(format("ScatterSimulation: down-sampled scanner info:\n{}", templ_proj_data_info_sptr->parameter_info()), 3);
947-
this->set_template_proj_data_info(*templ_proj_data_info_sptr);
956+
this->set_template_proj_data_info(templ_proj_data_info_sptr);
948957
this->set_output_proj_data(this->output_proj_data_filename);
949958

950959
return Succeeded::yes;

src/scatter_buildblock/scatter_detection_modelling.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ ScatterSimulation::find_detectors(unsigned& det_num_A, unsigned& det_num_B, cons
6565
error("ScatterSimulation::find_detectors: need to call set_up() first");
6666
#endif
6767
CartesianCoordinate3D<float> detector_coord_A, detector_coord_B;
68-
auto ptr = dynamic_cast<ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
68+
const auto* ptr = dynamic_cast<const ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
6969
if (ptr)
7070
{
7171
ptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, bin);
7272
}
7373
else
7474
{
75-
auto ptr = dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
75+
const auto* ptr = dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
7676
if (ptr)
7777
{
7878
ptr->find_cartesian_coordinates_of_detection(detector_coord_A, detector_coord_B, bin);

src/test/test_ScatterSimulation.cxx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ ScatterSimulationTests::test_downsampling_ProjDataInfo()
9292
false)));
9393

9494
unique_ptr<SingleScatterSimulation> sss(new SingleScatterSimulation());
95-
sss->set_template_proj_data_info(*original_projdata);
95+
sss->set_template_proj_data_info(original_projdata);
9696
{
9797
auto sss_projdata = dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(sss->get_template_proj_data_info_sptr().get());
9898
check(*original_projdata == *sss_projdata, "Check the ProjDataInfo has been set correctly.");
@@ -183,7 +183,7 @@ ScatterSimulationTests::test_downsampling_DiscretisedDensity()
183183

184184
unique_ptr<SingleScatterSimulation> sss(new SingleScatterSimulation());
185185

186-
sss->set_template_proj_data_info(*original_projdata);
186+
sss->set_template_proj_data_info(original_projdata);
187187
sss->set_density_image_sptr(atten_density);
188188

189189
// int total_scatter_points_orig = sss.get_num_scatter_points();
@@ -391,7 +391,7 @@ ScatterSimulationTests::test_scatter_simulation()
391391
//// sss settings
392392
sss->set_randomly_place_scatter_points(false);
393393

394-
sss->set_template_proj_data_info(*original_projdata_info);
394+
sss->set_template_proj_data_info(original_projdata_info);
395395
sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings(), -1);
396396
#if 1
397397
set_tolerance(.02);
@@ -448,7 +448,7 @@ ScatterSimulationTests::test_scatter_simulation()
448448
}
449449

450450
// a few tests with more downsampled scanner
451-
sss->set_template_proj_data_info(*original_projdata_info);
451+
sss->set_template_proj_data_info(original_projdata_info);
452452
sss->downsample_scanner(original_projdata_info->get_scanner_sptr()->get_num_rings() / 2, -1);
453453
{
454454
const int new_size_z = 14;

src/test/test_interpolate_projdata.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ InterpolationTests::transaxial_upsampling_interpolation_test_blocks()
850850

851851
// use the code in scatter simulation to downsample the scanner
852852
auto scatter_simulation = SingleScatterSimulation();
853-
scatter_simulation.set_template_proj_data_info(*proj_data_info);
853+
scatter_simulation.set_template_proj_data_info(proj_data_info);
854854
scatter_simulation.set_exam_info(exam_info);
855855
scatter_simulation.downsample_scanner(-1, 96 / 4); // number of detectors per ring reduced by factor of four
856856
auto downsampled_proj_data_info = scatter_simulation.get_template_proj_data_info_sptr();

0 commit comments

Comments
 (0)