Hi Thomas
After much investigation today, I have now added a flag to
GarbageCollecter to disable it for our use, as it really does not work
as intended for relatively complex code (I now manually track which
objects I create and need to call _kill_pointed for). There are two main
issues that we run into with the current GarbageCollector:
1. As already discussed, classes like the BRepPrimAPI and BRepAlgoAPI
have an internal "myShape" data member that gets destroyed when the
parent object's _kill_pointed method is called, causing all kinds of
issues. As an example, it makes utility functions like the one below
unusable, as the local BRepPrimAPI object goes out of scope and is thus
collected by GarbageCollector for purging, even though the caller of the
function may want to use the resulting shape for some time to come,
after the parent is "purged":
def MakeEdge(p1, p2):
gp1,gp2 = [gp_Pnt(x,y,z) for x,y,z in (p1,p2)]
mkedge = BRepBuilderAPI_MakeEdge(gp1, gp2)
return mkedge.Shape()
2. Every time a C++ OCC object is retrieved from Python in any way, a
brand new SWIG wrapper object is instantiated for it, very
easily/quickly leading to multiple wrapper objects on the Python side
for the same underlying C++ object (I see this especially when using OCC
shape containers repeatedly). When any one of these wrapper objects goes
out of scope and is collected and purged, the underlying C++ object is
destroyed (via _kill_pointed), even though there may well be other
Python wrapper objects still needing access to it.
I guess the ideal solution to this issue would be to keep a mapping
of C++ object -> SWIG wrapper object on the C++ side, so that there is
always a 1-to-1 correspondence between the two, but I'm not sure how
plausible this really is from an implementation point-of-view. As an
example of what I mean, consider this: every time you call
"mkedge.Shape()" in my example above, you get a new/different Python
object, wrapping the same underlying myShape instance, which is totally
unexpected and unintuitive from a Python perspective. A "pure" Python
programmer would expect to get the exact same Python object back every
time, and not different wrapper objects with just an "artificial"
identical hash value.
I realize that this is a very difficult issue, and that as users of the
PythonOCC wrapper library we will need to take responsibility for memory
management issues, but I can see lots of confusion for Python
programmers using the library and expecting "Python-like" behaviour out
of the box.
I wish I new more about the SWIG wrapper object instantiation process so
that I could make more concrete suggestions, instead of just complaining
;-) What is your gut-feeling about this Thomas - do you think these two
issues can be addressed in the wrapper, or is the current way the only
viable implementation? If you can give me a short description of the
SWIG wrapper instantiation process, I may be able to delve into it myself.
Thanks for your help with this thus far,
Frank
On 16/03/2010 9:04 AM, Thomas Paviot wrote:
Hi Frank,
I also suggest you use the push_context() and pop_context() methods at
the beginning and the end of your algorithm. garbage.push_context()
creates a new collector. Objects collected will go to this new list.
Then, at the end of your algorithm, just call .smart_purge() and then
pop_context(). Otherwise, when calling the smart_purge() method,
you'll erase all other objects that might have been stored in the gc
thus causing strange things.
def my_high_memory_consuming_algorithm():
GarbageCollector.garbage.push_context()
... do whatever you want ...
GarbageCollector.garbage.smart_purge() #only the objects created
since the call to push_context are erased. Use MMGT_OPT=0 to make sure
freeing #memory.
GarbageCollector.pop_context()
return data
There's also a garbage.prevent_object(object) method, that will take
care to not delete an object that it's important to keep reference.
These three functions are quite new (but validated with a unittest).
I'm curious to have your feedback!
Regards,
Thomas
These two functions are quite new,
2010/3/16 Frank Conradie <fr...@qfin.net <mailto:fr...@qfin.net>>
Hi Thomas
Well, I'm glad it's not a bug then! I can think of a few ways to
handle this situation in my algorithm, so it should not be a
problem. I'm just wondering how many other classes behave in this
"unexpected" way :O
Cheers,
Frank
On 15/03/2010 10:26 PM, Thomas Paviot wrote:
Hi Frank,
This behaviour does not come from SWIG but from OpenCASCADE
itself. Let me explain you in a few words what happens here.
The BRepPrimAPI_MakeBox class inherits from the
BRepBuilderAPI_MakeShape one. This class provides the Shape()
method you can find in BRepPrimAPI_Make* basic geometry
constructors. If you have a look at the Shape() method in the
file BRepBuilderAPI_MakeShape.cxx, you will read:
"""
//=======================================================================
//function : Shape
//purpose :
//=======================================================================
const TopoDS_Shape& BRepBuilderAPI_MakeShape::Shape() const
{
if (!IsDone()) {
// the following is const cast away
((BRepBuilderAPI_MakeShape*) (void*) this)->Build();
Check();
}
return myShape;
}
"""
The Shape() method then returns a reference to the protected
attribute 'myShape' of the class BRepBuilderAPI_MakeBox. In other
words, if you delete the BRepPrimAPI_MakeBox instance, you loose
the reference to the shape.
For instance:
>>> from OCC.BRepPrimAPI_MakeBox import *
>>> from OCC.TopoDS import *
>>> box = BRepPrimAPI_MakeBox(10,10,10)
>>> shp = box.Shape()
>>> print shp.IsNull()
0
>>> del box #object is garbage collected, not deleted yet
>>> print shp.IsNull()
0
>>> GarbageCollector.garbage.smart_purge() # all objects are
deleted, loose the reference to myShape attribute
>>> print shp.IsNull()
1
Then force objects deletion only if you're sure you don't need
the shapes anymore. I don't know what you're trying to do
exactly, so it's difficult to suggest any best practice.
Regards,
Thomas
2010/3/15 Frank Conradie <fr...@qfin.net <mailto:fr...@qfin.net>>
Hi Thomas
I am getting crash behaviour in the interim 0.5 PythonOCC
release,
related to the new GarbageCollector implementation. The code
below
crashes on my PC:
----------------------------------------
from OCC.gp import *
from OCC.BRepPrimAPI import *
from OCC.BRepBuilderAPI import *
from OCC.TopExp import *
from OCC.TopoDS import *
o = 0.0
w = 0.1
fp1 = (o,o,o)
fp2 = (w,w,w)
mkbox = BRepPrimAPI_MakeBox(gp_Pnt(fp1[0],fp1[1],fp1[2]),
gp_Pnt(fp2[0],fp2[1],fp2[2]))
s = mkbox.Shape()
del mkbox
print s.ShapeType()
GarbageCollector.garbage.smart_purge()
# The next line crashes my system
print s.ShapeType()
----------------------------------------
It is almost as if calling _kill_pointed for the mkbox causes
shape "s"
to become "corrupted" in some way. I dug through the SWIG
code a bit,
but cannot see anything obviously wrong, so I was hoping you
could shed
some light on the issue.
By the way, I did get the SVN compiled myself as well, and
get the same
crash with my compiled lib as well as the one you made
available on your
website.
Thanks,
Frank
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org <mailto:Pythonocc-users@gna.org>
https://mail.gna.org/listinfo/pythonocc-users
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org <mailto:Pythonocc-users@gna.org>
https://mail.gna.org/listinfo/pythonocc-users
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org <mailto:Pythonocc-users@gna.org>
https://mail.gna.org/listinfo/pythonocc-users
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org
https://mail.gna.org/listinfo/pythonocc-users
_______________________________________________
Pythonocc-users mailing list
Pythonocc-users@gna.org
https://mail.gna.org/listinfo/pythonocc-users