Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions bindings/pyroot/pythonizations/test/regression_23728.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ def test_exit_cleanup_after_interpreted_destructor(self):
with ROOT.TFile(fname, "RECREATE") as f:
t = ROOT.TTree("t", "t")
v = ROOT.std.vector[ROOT.std.vector[int]]()
t.Branch("vv", v)
# Use the overload taking the class name and the address: the
# templated Branch() refuses collections without a compiled
# CollectionProxy, which would silently leave the tree without
# the branch and the test without its purpose.
branch = t.Branch("vv", "vector<vector<int> >", ROOT.AddressOf(v))
self.assertTrue(branch, "Branch() refused to create the branch")
v.push_back(ROOT.std.vector[int]([1, 2]))
t.Fill()
f.Write()
Expand All @@ -53,8 +58,22 @@ def test_exit_cleanup_after_interpreted_destructor(self):
if t.GetDirectory() != f:
raise RuntimeError("the tree is not owned by the file")

# Make the interpreter JIT-compile a destructor before exit.
ROOT.std.vector[ROOT.TNamed]()
# Sanity checks that the branch exists and that bytes are
# actually read, which is required for the reproducer.
if not t.GetBranch("vv"):
raise RuntimeError("the tree has no 'vv' branch")
if t.GetEntry(0) <= 0:
raise RuntimeError("no bytes read for entry 0: no branch object was created")

# Make the interpreter JIT-compile the destructor of the branch
# class before exit. This creates the destructor cache before the
# end-of-process cleanup needs it (and, before the fix, destroyed
# it again before that). Using the very same class keeps the
# exit-time destructor call a pure cache lookup: compiling code
# at that point is not possible on Windows, where the atexit
# handler of libCore runs only when libCling's statics have
# already been torn down by its DLL_PROCESS_DETACH.
ROOT.std.vector[ROOT.std.vector[int]]()

print("end of script")
"""
Expand All @@ -65,12 +84,16 @@ def test_exit_cleanup_after_interpreted_destructor(self):
# whatever the heap layout and the interpreter internals.
env = dict(os.environ, MALLOC_PERTURB_="85")

# Without the fix, ROOT's crash handler can hang on the way out
# of the segfault instead of terminating the process: fail
# instead of waiting for the ctest timeout.
res = subprocess.run(
[sys.executable, "-c", script],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
env=env,
timeout=300,
)

self.assertEqual(res.returncode, 0, msg=res.stdout)
Expand Down
Loading