Skip to content

Commit b2cb5ef

Browse files
[Python] Fix double free of objects added to TH1::fFunctions (#22493)
Fixes #22417 Provide the correct signal to the python bindings that calling `th1->GetListOfFunctions()->Add(object)` will take ownership of the object. ROOT's Python bindings use TCollection::IsOwner to decide whether objects inserted into a collection (like `TH1::fFunctions`) should be deleted (see `_TCollection_Add` pythonization). To ensure the Python bindings get the correct ownership signal, set the ownership on TH1::fFunctions. Although, in reality, TH1's destructor actually handles ownership, this solution still works, since the destructor removes all entries before deleting the collection.
1 parent 186d603 commit b2cb5ef

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

bindings/pyroot/pythonizations/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ ROOT_ADD_PYUNITTEST(pyroot_pyz_th1 th1.py)
6565
# The above tests a deadlock. It should complete in about 1s. If we don't reduce the timeout, we need to wait 1500 s.
6666
set_tests_properties(pyunittests-bindings-pyroot-pythonizations-pyroot-pyz-th1 PROPERTIES TIMEOUT 30)
6767
ROOT_ADD_PYUNITTEST(pyroot_pyz_th1_fillN th1_fillN.py PYTHON_DEPS numpy)
68+
ROOT_ADD_PYUNITTEST(pyroot_pyz_th1_addfunction th1_addfunction.py)
6869
ROOT_ADD_PYUNITTEST(pyroot_pyz_th2_fillN th2_fillN.py PYTHON_DEPS numpy)
6970
ROOT_ADD_PYUNITTEST(pyroot_pyz_th2 th2.py)
7071
ROOT_ADD_PYUNITTEST(pyroot_pyz_th3 th3.py)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import unittest
2+
3+
import ROOT
4+
5+
6+
class TH1AddFunction(unittest.TestCase):
7+
def test_adding_a_function_does_not_segfault(self):
8+
"""
9+
This test verifies that box is only freed once
10+
"""
11+
h = ROOT.TH1F("h", "h", 10, 0, 1)
12+
box = ROOT.TBox(0.1, 0.1, 0.9, 0.9)
13+
h.GetListOfFunctions().Add(box)
14+
15+
16+
if __name__ == "__main__":
17+
unittest.main()

hist/hist/src/TH1.cxx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,14 @@ TH1::TH1()
623623
{
624624
fDirectory = nullptr;
625625
fFunctions = new TList;
626+
// ROOT's Python bindings use TCollection::IsOwner to decide whether objects
627+
// inserted into a collection (like `TH1::fFunctions`) should be deleted
628+
// (see `_TCollection_Add` pythonization). To ensure the Python bindings
629+
// get the correct ownership signal, set the ownership on TH1::fFunctions.
630+
// Although, in reality, TH1's destructor actually handles ownership, this
631+
// solution still works, since the destructor removes all entries before
632+
// deleting the collection.
633+
fFunctions->SetOwner(true);
626634
fNcells = 0;
627635
fIntegral = nullptr;
628636
fPainter = nullptr;
@@ -804,6 +812,14 @@ void TH1::Build()
804812
SetTitle(fTitle.Data());
805813

806814
fFunctions = new TList;
815+
// ROOT's Python bindings use TCollection::IsOwner to decide whether objects
816+
// inserted into a collection (like `TH1::fFunctions`) should be deleted
817+
// (see `_TCollection_Add` pythonization). To ensure the Python bindings
818+
// get the correct ownership signal, set the ownership on TH1::fFunctions.
819+
// Although, in reality, TH1's destructor actually handles ownership, this
820+
// solution still works, since the destructor removes all entries before
821+
// deleting the collection.
822+
fFunctions->SetOwner(true);
807823

808824
UseCurrentStyle();
809825

0 commit comments

Comments
 (0)