saviopalmieri / ctypes-opencv

Automatically exported from code.google.com/p/ctypes-opencv
0 stars 0 forks source link

Replace pointer with c_void_p/addressof in __del__ methods #8

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
The automatic memory management is a key feature of ctypes-opencv, but the
use of pointer() in the __del__ methods appears fragile during interpreter
shutdown.

I was getting a lot of stray exceptions during interactive experimentation
upon existing the interpreter, occurring within the IplImage.__del__
method.  Depending on the sequence of the removal of global objects (such
as modules), function dependencies of __del__ methods may no longer be met.
 While not my original problem case, assume the following two modules:

bad_parent.py:

    import bad

bad.py:

    import opencv as cv
    t = cv.cvCreateImage(cv.cvSize(320,240), 8, 3)

Then, under Python 2.5 (2.5.1 in the below example), running bad_parent.py
will generate an exception when t attempts to clean itself up:

> python bad_parent.py
Exception exceptions.TypeError: "'NoneType' object is not callable" in
<bound method IplImage.__del__ of
IplImage(nSize=112,ID=0,nChannels=3,alphaChannel=0,depth=8,colorModel='RGB',chan
nelSeq='BGR',dataOrder=0,origin=0,align=4,width=320,height=240,roi=<ctypes.LP_Ip
lROI
object at 0x00985E40>,maskROI=<ctypes.LP_IplImageobject at
0x00985E40>,imageID=None,tileInfo=<ctypes.LP_IplTileInfo object at
0x00985DF0>,imageSize=230400,widthStep=960,BorderMode=<opencv.cxcore.c_long_Arra
y_4
object at 0x00985DA0>,BorderConst=<opencv.cxcore.c_long_Array_4 object at
0x00985DA0>)> ignored

I tracked it down to the fact that pointer(IplImage) had never been called
during execution, so it had no cached pointer type.  In order to create a
new pointer type it tries to use_ctypes._Pointer(), but somewhere in that
process it runs into something that has probably been cleared already by
the interpreter.

Note that the issue is tricky - it only occurs with some module names and
import combinations since that affects when other modules are destroyed at
shutdown.  For example, calling bad.py "working.py" instead avoids the
error.  Also, just adding a call to pointer(IplImage) inside of bad.py
fixes things since the pointer type is then cached.  And finally python
2.6/3.0 seem to handle even the "bad" case, but I presume it's likely that
other configurations might tickle poor behavior in those versions.

Since such __del__ exceptions are ignored by the interpreter, they're
arguably unimportant, but depending on what is happening at object release
time it may be a problem as nothing past the exception point in __del__
runs.  So, for example, for CvVideoWriter it might result in not calling
cvReleaseVideoWriter which would leave a corrupted file.

Since all the destructor is trying to do is get a pointer-to-pointer to the
object being destroyed, I'd like to suggest skipping past pointer (which
has to create the pointer type), and just create a void * with the address
of the object needing to be released.  The attached patch (against trunk
r139) has that change.

Note that I've actually run into cases during an object's destructor where
globals from its own module were no longer available - so even referencing
names like addressof() or _cvReleaseImage might theoretically during
interpreter shutdown.  I haven't actually seen that yet, but if so, the
best way to work around that is to cache references to the objects needed
during destruction during instance construction so there's no dependence on
module level entities during destruction.

Original issue reported on code.google.com by db3l....@gmail.com on 28 Dec 2008 at 12:39

Attachments:

GoogleCodeExporter commented 8 years ago
Hmm. This is very interesting. I have never observed such phenomenon in my 
Python 2.5
and 2.6. Patched. Thanks.

Original comment by pmtri80@gmail.com on 28 Dec 2008 at 1:47