I have a rather large pyROOT based GUI program. I decided to make use of a file dialog displayed on a menu selection. Here is the responsible part of the code:
however, the code works… randomly. Sometimes saves the file and the program continues to work normally, sometimes does not save the file and freezes the GUI. It happens similarily in both the uncommented and commented parts. Can you derive from this code snippet what am I doing wrong?
there has been some work in fixing locking to handle thread safety. PyROOT has always locked CINT before calling. There are multiple layers of callbacks (through CINT) in your code.
Which version of ROOT are you using? There are a few recent version that are affected (basically between those extra locks going in, and them being removed from PyROOT).
Of course, it could also be something completely different …
5.34.15 should not have the problem that I was thinking about.
The biggest problem is probably with ProcessLine(). Is there any way you can do without? Or rather, why is it used in the first place? The TGFileInfo() call gives a warning, but that can be suppressed if you like. Instead of malloc, you can use an array.array of type ‘c’.
then I have no further ideas. You can run under gdb and get all the stack traces of the individual threads to find out where the block is. Maybe there’s something obvious in there.
Otherwise, I’d have to be able to reproduce this (i.e. need some code that exhibits the problem).
I am not sure how to debug pyROOT script with GDB, so I removed 99% of my code and still having the same behaviour. I attach the file. If you could please try it. Just run and select from menu “Save display” and than save as, for example, 1.png.
Sometimes it takes more than 10 attempts to save/quit/save/quit… until it hangs, sometimes it hangs on the first attempt. etos.py (4.71 KB)
I know this line is commented out, but just for the record/those who come after:ROOT.gROOT.ProcessLine("fi.fIniDir = (char*)malloc(%d);" % (len(ini_dir)+1))fIniDir is actually deleted with delete[], so it needs to be allocated with new[].
Yes, the error is when creating the TGFileDialog. Valgrind claims bad writes onto the newly created object. I don’t understand that. I figured it might have something to do with a failing cast (the object has multiple inheritance op its ancestor tree, and CINT can’t handle that, unless the offset is zero; as it happens, that is the case, though).
Well, I can see now what is going on, but don’t know yet what to do about it.
Here’s what’s happening: the constructor of the TGFileDialog runs the whole dialog business and at the end of that, the dialog object gets deleted. Only then does the constructor return, and so the object handed to python is deleted already. The memory regulator, thinking it is a fresh object, then scribbles in that deleted space (getting valgrind up in arms) by calling its SetBit() method. That scribbling itself is not a problem, as there is insufficient code in between to have the memory reused, but it is at the start of the object, and thus overwriting malloc’s memory management info.
Pfff …
Best workaround I can think of, is to call new TGFileDialog on a gROOT.ProcessLine(), so that there is never a Python side object for the file dialog. Or better yet, do all of that in a C++ helper.
I tried to do in the helper, unfortunately I get “error: ‘fi’ does not name a type”, where fi is TGFileInfo. I confirmed, that the code works in standalone CINT. I attach the helper. save_display.C (609 Bytes)
it’s a limitation of CINT that it can not define functions at run-time. This is fixed with Cling in ROOT6, or use of ACLiC with CINT.
However, I think this may be the simplest:fi = ROOT.gROOT.ProcessLine("TGFileInfo* fi = new TGFileInfo; new TGFileDialog(gClient->GetRoot(), gClient->GetRoot(), kFDSave, fi); fi;")
self.fi = ROOT.BindObject(fi, ROOT.TGFileInfo)
ROOT.SetOwnership(self.fi, True)
ROOT.gPad.SaveAs(self.fi.fFilename)
Cheers,
Wim
I didn’t see a callable method in the .C, so I wouldn’t expect that the work with ACLiC, so that’s why I assumed you used CINT rather. Doesn’t matter.
To suppress the warning, do:import warnings
warnings.filterwarnings( action='ignore', category=RuntimeWarning, \
message='creating converter for unknown type "const char\*\*"' )
Cheers,
Wim