From 9ad3b4d23e5b2e0d509218b3408b8983bc6bae61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 12:42:38 -0300 Subject: [PATCH 1/8] Added cleanup method to remove cells from attackers pAttackTarget pointer --- core/PhysiCell_cell.cpp | 13 +++++++++++++ core/PhysiCell_cell.h | 1 + 2 files changed, 14 insertions(+) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index a14c70b99..4fc738a92 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -1207,6 +1207,9 @@ void delete_cell( int index ) // released internalized substrates (as of 1.5.x releases) pDeleteMe->release_internalized_substrates(); + // new Dec 2, 2025 + pDeleteMe->remove_self_from_attackers(); + // performance goal: don't delete in the middle -- very expensive reallocation // alternative: copy last element to index position, then shrink vector by 1 at the end O(constant) @@ -3433,6 +3436,16 @@ void Cell::remove_all_spring_attachments( void ) return; } +void Cell::remove_self_from_attackers( void ) +{ + #pragma omp parallel for + for (int j=0; j < all_cells->size(); j++) + { + if (j != index && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget != NULL && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget == this) { + (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget = NULL; + } + } +} void attach_cells( Cell* pCell_1, Cell* pCell_2 ) { diff --git a/core/PhysiCell_cell.h b/core/PhysiCell_cell.h index 5b7c52d96..4de5fcebe 100644 --- a/core/PhysiCell_cell.h +++ b/core/PhysiCell_cell.h @@ -238,6 +238,7 @@ class Cell : public Basic_Agent void attach_cell_as_spring( Cell* pAddMe ); // done void detach_cell_as_spring( Cell* pRemoveMe ); // done void remove_all_spring_attachments( void ); // done + void remove_self_from_attackers( void ); // I want to eventually deprecate this, by ensuring that // critical BioFVM and PhysiCell data elements are synced when they are needed From 5fbcfc2c51b366fe225028a548887f7b3ce58d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 12:46:32 -0300 Subject: [PATCH 2/8] Added hacky cleanup to fix use after free for neighbors and spring attachments --- core/PhysiCell_cell.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index 4fc738a92..cea4cc67b 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3404,6 +3404,25 @@ void Cell::remove_self_from_all_neighbors( void ) else { /* future error message */ } } + + // This is a bit of a ugly hack, we need to find the origin of that bug + #pragma omp parallel for + for ( int i = 0; i < all_cells->size(); i++ ) + { + Cell* pC = (*all_cells)[i]; + if (pC != this) + { + auto SearchResult = std::find( + pC->state.neighbors.begin(),pC->state.neighbors.end(),this ); + if ( SearchResult != pC->state.neighbors.end() ) + { + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; + pC->state.neighbors.erase( SearchResult ); + } + + } + } + return; } @@ -3433,6 +3452,25 @@ void Cell::remove_all_spring_attachments( void ) // clear my list state.spring_attachments.clear(); } + + // This is a bit of a ugly hack, we need to find the origin of that bug + #pragma omp parallel for + for ( int i = 0; i < all_cells->size(); i++ ) + { + Cell* pC = (*all_cells)[i]; + if (pC != this) + { + auto SearchResult = std::find( + pC->state.spring_attachments.begin(),pC->state.spring_attachments.end(),this ); + if ( SearchResult != pC->state.spring_attachments.end() ) + { + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; + pC->state.spring_attachments.erase( SearchResult ); + } + + } + } + return; } From 74e24c517b81a98f87f2eba3d2a0c778a212577b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 14:45:26 -0300 Subject: [PATCH 3/8] Fix spring attachment cleanup message Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- core/PhysiCell_cell.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index cea4cc67b..8dde54796 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3464,7 +3464,10 @@ void Cell::remove_all_spring_attachments( void ) pC->state.spring_attachments.begin(),pC->state.spring_attachments.end(),this ); if ( SearchResult != pC->state.spring_attachments.end() ) { - std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; + #pragma omp critical + { + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; + } pC->state.spring_attachments.erase( SearchResult ); } From ca591c72d914512f3abf5a20d417f8c63a61ac75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 14:46:24 -0300 Subject: [PATCH 4/8] Fix neighbors cleanup message Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- core/PhysiCell_cell.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index 8dde54796..3eb7b8eaf 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3416,7 +3416,10 @@ void Cell::remove_self_from_all_neighbors( void ) pC->state.neighbors.begin(),pC->state.neighbors.end(),this ); if ( SearchResult != pC->state.neighbors.end() ) { - std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; + #pragma omp critical + { + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; + } pC->state.neighbors.erase( SearchResult ); } From 0780ef2ba8e03fdf6f928e0abfc3b4ec2fe8534a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 14:50:01 -0300 Subject: [PATCH 5/8] Implement comparison simplification for pAttackTarget Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- core/PhysiCell_cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index 3eb7b8eaf..e4ff5ab38 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3485,7 +3485,7 @@ void Cell::remove_self_from_attackers( void ) #pragma omp parallel for for (int j=0; j < all_cells->size(); j++) { - if (j != index && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget != NULL && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget == this) { + if (j != index && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget == this) { (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget = NULL; } } From 73acd594bb796601f8a83286c0045dce09d90e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 14:54:58 -0300 Subject: [PATCH 6/8] Erase now also within omp critical block --- core/PhysiCell_cell.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index e4ff5ab38..d40525c82 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3419,8 +3419,8 @@ void Cell::remove_self_from_all_neighbors( void ) #pragma omp critical { std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; + pC->state.neighbors.erase( SearchResult ); } - pC->state.neighbors.erase( SearchResult ); } } @@ -3470,8 +3470,8 @@ void Cell::remove_all_spring_attachments( void ) #pragma omp critical { std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; + pC->state.spring_attachments.erase( SearchResult ); } - pC->state.spring_attachments.erase( SearchResult ); } } From 6093eea4fd33e06f14ccb460d0dc13ada4c4de73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Tue, 2 Dec 2025 14:58:39 -0300 Subject: [PATCH 7/8] Compare cell pointer and not the index --- core/PhysiCell_cell.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index d40525c82..91a4f8723 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3485,8 +3485,9 @@ void Cell::remove_self_from_attackers( void ) #pragma omp parallel for for (int j=0; j < all_cells->size(); j++) { - if (j != index && (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget == this) { - (*all_cells)[j]->phenotype.cell_interactions.pAttackTarget = NULL; + Cell* pC = (*all_cells)[j]; + if (( pC != this) && pC->phenotype.cell_interactions.pAttackTarget == this) { + pC->phenotype.cell_interactions.pAttackTarget = NULL; } } } From b7ccc391831ececa83a0a9f0f305f0ce93b0e06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20No=C3=ABl?= Date: Wed, 3 Dec 2025 10:36:35 -0300 Subject: [PATCH 8/8] removing omp parallel look for cleanup --- core/PhysiCell_cell.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index 91a4f8723..e787bb717 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -3406,7 +3406,6 @@ void Cell::remove_self_from_all_neighbors( void ) } // This is a bit of a ugly hack, we need to find the origin of that bug - #pragma omp parallel for for ( int i = 0; i < all_cells->size(); i++ ) { Cell* pC = (*all_cells)[i]; @@ -3416,13 +3415,9 @@ void Cell::remove_self_from_all_neighbors( void ) pC->state.neighbors.begin(),pC->state.neighbors.end(),this ); if ( SearchResult != pC->state.neighbors.end() ) { - #pragma omp critical - { - std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; - pC->state.neighbors.erase( SearchResult ); - } + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl; + pC->state.neighbors.erase( SearchResult ); } - } } @@ -3457,7 +3452,6 @@ void Cell::remove_all_spring_attachments( void ) } // This is a bit of a ugly hack, we need to find the origin of that bug - #pragma omp parallel for for ( int i = 0; i < all_cells->size(); i++ ) { Cell* pC = (*all_cells)[i]; @@ -3467,11 +3461,8 @@ void Cell::remove_all_spring_attachments( void ) pC->state.spring_attachments.begin(),pC->state.spring_attachments.end(),this ); if ( SearchResult != pC->state.spring_attachments.end() ) { - #pragma omp critical - { - std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; - pC->state.spring_attachments.erase( SearchResult ); - } + std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl; + pC->state.spring_attachments.erase( SearchResult ); } }