Skip to content

Commit a96f16a

Browse files
FIX: Fix Linux segfault on clear scene
- Fix segfault when clearing scene due to std::map erase in for range loop
1 parent 41369e8 commit a96f16a

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

LayerDM/MRMLDM/vtkMRMLLayerDMPipelineManager.cxx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,21 @@ vtkSmartPointer<vtkMRMLLayerDMPipelineI> vtkMRMLLayerDMPipelineManager::GetNodeP
282282
return found->second;
283283
}
284284

285+
int vtkMRMLLayerDMPipelineManager::GetNumberOfPipelines() const
286+
{
287+
return this->m_pipelineMap.size();
288+
}
289+
290+
vtkMRMLLayerDMPipelineI* vtkMRMLLayerDMPipelineManager::GetNthPipeline(int iPipeline) const
291+
{
292+
if (iPipeline < 0 || iPipeline >= this->m_pipelineMap.size())
293+
{
294+
return nullptr;
295+
}
296+
297+
return std::next(this->m_pipelineMap.begin(), iPipeline)->second;
298+
}
299+
285300
void vtkMRMLLayerDMPipelineManager::SetRenderer(vtkRenderer* renderer) const
286301
{
287302
// Pass the renderer to the camera sync
@@ -306,13 +321,19 @@ void vtkMRMLLayerDMPipelineManager::RemoveOutdatedPipelines()
306321
return;
307322
}
308323

324+
std::vector<vtkWeakPointer<vtkMRMLNode>> outdatedPipelines;
309325
for (const auto& pipe : m_pipelineMap)
310326
{
311327
if (!pipe.first || !this->m_scene->GetNodeByID(pipe.first->GetID()))
312328
{
313-
this->RemovePipeline(pipe.first);
329+
outdatedPipelines.emplace_back(pipe.first);
314330
}
315331
}
332+
333+
for (const auto& pipe : outdatedPipelines)
334+
{
335+
this->RemovePipeline(pipe);
336+
}
316337
}
317338

318339
void vtkMRMLLayerDMPipelineManager::AddMissingPipelines()

LayerDM/MRMLDM/vtkMRMLLayerDMPipelineManager.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ class VTK_SLICER_LAYERDM_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT vtkMRMLLayerDMPipe
6363
/// Returns the pipeline associated with the input display node if any.
6464
vtkSmartPointer<vtkMRMLLayerDMPipelineI> GetNodePipeline(vtkMRMLNode* node) const;
6565

66+
/// Returns the number of pipelines currently managed by the pipeline manager
67+
int GetNumberOfPipelines() const;
68+
69+
/// Returns the list of currently managed display nodes of the pipeline manager.
70+
///
71+
/// \sa GetNodePipeline
72+
vtkMRMLLayerDMPipelineI* GetNthPipeline(int iPipeline) const;
73+
6674
/// @{
6775
/// Makes the latest pipeline lose focus
6876
void LoseFocus() const;

LayerDM/Testing/Python/PipelineManagerTest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,36 @@ def test_on_pipeline_removed_triggers_renderer_removed(self):
263263

264264
self.pipelineManager.RemoveNode(m1.GetDisplayNode())
265265
m1.mockOnRendererRemoved.assert_called_once_with(self.defaultRenderer)
266+
267+
def test_correctly_cleans_up_outdated_pipelines(self):
268+
modelNodes = [slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode") for _ in range(5)]
269+
for modelNode in modelNodes:
270+
self.pipelineManager.AddNode(modelNode)
271+
pipelines = [self.pipelineManager.GetNthPipeline(i_pipe) for i_pipe in range(self.pipelineManager.GetNumberOfPipelines())]
272+
modelPipelines = [p for p in pipelines if p.GetDisplayNode() in modelNodes]
273+
assert len(modelPipelines) == 5
274+
prevNumber = self.pipelineManager.GetNumberOfPipelines()
275+
slicer.mrmlScene.RemoveNode(modelNodes[0])
276+
slicer.mrmlScene.RemoveNode(modelNodes[1])
277+
slicer.mrmlScene.RemoveNode(modelNodes[2])
278+
assert self.pipelineManager.GetNumberOfPipelines() == prevNumber
279+
self.pipelineManager.UpdateFromScene()
280+
assert self.pipelineManager.GetNumberOfPipelines() == prevNumber - 3
281+
282+
def test_notifies_pipelines_when_references_are_added_or_removed(self):
283+
# Create a display markups node
284+
m1 = MockPipeline()
285+
self.nextMock = m1
286+
dNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialDisplayNode")
287+
assert self.pipelineManager.AddNode(dNode)
288+
289+
# Create a markups node
290+
markups = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
291+
292+
# Set the markups node display to the previous display node and expect the pipeline to have been notified
293+
markups.SetAndObserveDisplayNodeID(dNode.GetID())
294+
m1.mockOnReferenceToDisplayNodeAdded.assert_called_once_with(markups, "display")
295+
296+
# Unset the display node and expect the pipeline to have been notified
297+
markups.SetAndObserveDisplayNodeID("")
298+
m1.mockOnReferenceToDisplayNodeRemoved.assert_called_once_with(markups, "display")

0 commit comments

Comments
 (0)