Deletion timing of Geo objects in new PyROOT


ROOT Version: 6.22/00
Platform: macOS 10.15.5
Compiler: Apple clang version 11.0.3 (clang-1103.0.32.62)


When I use the Geom library in PyROOT in the following short test script, I get a segmentation fault presumably due to conflict between the Python garbage collection and the automatic memory deletion in the Geom library.

Could you tell me a recommended way to fix this script?

import ROOT

def makeTheWorld():
    manager = ROOT.TGeoManager("manager", "manager")
    matVacuum = ROOT.TGeoMaterial("matVacuum", 0, 0, 0)
    vacuum = ROOT.TGeoMedium("Vacuum", 1, matVacuum)

    world = manager.MakeBox("world", vacuum, 1, 1, 1)
    manager.SetTopVolume(world)
    
    return manager

def test():
    manager = makeTheWorld()
    smallbox = manager.MakeBox("smallbox", manager.GetMedium("Vacuum"),
                               0.1, 0.1, 0.1)
    manager.GetTopVolume().AddNode(smallbox, 1)
    manager.CloseGeometry()

test()
$ python3 test.py 
Info in <TGeoManager::TGeoManager>: Geometry manager, manager created
(snip)
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
Error in <THashList::Delete>: A list is accessing an object (0x7febd6539080) already deleted (list name = THashList)
Python(36663,0x1045b2dc0) malloc: tiny_free_list_remove_ptr: Internal invariant broken (next ptr of prev): ptr=0x7febd65523c0, prev_next=0x207febd65523c0
Python(36663,0x1045b2dc0) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort      python3 test.py
$ python3 test.py
Info in <TGeoManager::TGeoManager>: Geometry manager, manager created
(snip)
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
 *** Break *** segmentation violation
[/usr/local/root-6.22.00/obj/lib/libCore.so] TUnixSystem::DispatchSignals(ESignals) (no debug info)
[/usr/lib/system/libsystem_platform.dylib] _sigtramp (no debug info)
[<unknown binary>] (no debug info)
[/usr/local/root-6.22.00/obj/lib/libCore.so] TFunction::~TFunction() (no debug info)
[/usr/local/root-6.22.00/obj/lib/libcppyy_backend3_7.so] (anonymous namespace)::ApplicationStarter::~ApplicationStarter() (no debug info)
[/usr/lib/system/libsystem_c.dylib] __cxa_finalize_ranges (no debug info)
[/usr/lib/system/libsystem_c.dylib] exit (no debug info)
[/usr/lib/system/libdyld.dylib] start (no debug info)
[<unknown binary>] (no debug info)
 *** Break *** segmentation violation
[/usr/local/root-6.22.00/obj/lib/libCore.so] TUnixSystem::DispatchSignals(ESignals) (no debug info)
[/usr/lib/system/libsystem_platform.dylib] _sigtramp (no debug info)
[<unknown binary>] (no debug info)
[/usr/local/root-6.22.00/obj/lib/libCore.so] TFunction::~TFunction() (no debug info)
[/usr/local/root-6.22.00/obj/lib/libcppyy_backend3_7.so] (anonymous namespace)::ApplicationStarter::~ApplicationStarter() (no debug info)
[/usr/lib/system/libsystem_c.dylib] __cxa_finalize_ranges (no debug info)
[/usr/lib/system/libsystem_c.dylib] exit (no debug info)
[/usr/lib/system/libdyld.dylib] start (no debug info)
[<unknown binary>] (no debug info)

Hi,

It seems it is the TGeoMaterial object that is being deleted twice, although I don’t know exactly where in Geom this happens.

I don’t see anymore the error if I do:

def makeTheWorld():
    manager = ROOT.TGeoManager("manager", "manager")
    matVacuum = ROOT.TGeoMaterial("matVacuum", 0, 0, 0)
    ROOT.SetOwnership(matVacuum, False)  # Disable Python ownership of matVacuum
    vacuum = ROOT.TGeoMedium("Vacuum", 1, matVacuum)

    world = manager.MakeBox("world", vacuum, 1, 1, 1)
    manager.SetTopVolume(world)
    
    return manager

Thank you.

Here is another example to cause a segmentation fault, where TGeoMaterial is not implicitly used anymore, but other TGeo class constructors are called.

Do I have to change the ownership manually every time when I create a TGeo object in PyROOT? It is of course very easy to do it in this short example, but it is not (or troublesome) in actual cases.

import ROOT

def makeTheWorld():
    manager = ROOT.TGeoManager("manager", "manager")
    worldbox = ROOT.TGeoBBox("worldbox", 1, 1, 1)
    world = ROOT.TGeoVolume("world", worldbox)
    manager.SetTopVolume(world)
    
    ROOT.SetOwnership(worldbox, False)
    # ROOT.SetOwnership(world, False)
    
    return manager

def test():
    manager = makeTheWorld()
    smallbox = ROOT.TGeoBBox("smallbox", .1, .1, .1)
    small = ROOT.TGeoVolume("small", smallbox)
    manager.GetTopVolume().AddNode(small, 1)
    manager.CloseGeometry()

    ROOT.SetOwnership(smallbox, False)
    # ROOT.SetOwnership(small, False)

test()

Hi @oxon,
Enric is currently off, please ping us again if you don’t hear anything in the next couple of weeks, or feel free to open a jira ticket about the crash.

Thank you for the report with a clear reproducer by the way!
Cheers,
Enrico

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

Hi,

Indeed it seems that there are multiple cases in which TGeo C++ internally takes ownership of some objects and causes double deletion from Python. I will share this post with Andrei Gheata to see if he can give more info about such cases.

The only way I see this can be solved is either with the explicit release of the ownership in Python or by doing it implicitly via pythonizations of some TGeo constructors. For the second option, please open a JIRA ticket as Enrico suggested.

Hi, TGeoManager takes ownership of TGeoShape, TGeoMaterial, TGeoMedium and TGeoMatrix - derived classes. The only clean solution as mentioned by Enric is pythonizing the relevant constructors and/or registering methods, meanwhile python ownership has to be explicitly released for these objects.

Thank you all. I followed the advise to manually change the object ownership for the moment.

This topic was automatically closed 13 days after the last reply. New replies are no longer allowed.