3.2 Open Inventor Reference Counting

Open Inventor requires that the C++ programmer knows when and how to take care of reference counting of SoBase and its subclasses by calling the member functions ref(), unref(), and unrefNoDelete().

Python bindings with the same requirement will lead to program crashes when the Python programmer makes a mistake and Open Inventor objects get deleted without the Python bindings expecting it.

Therefore, IVuPy takes care of the Open Inventor reference counts and use of ref(), unref(), and unrefNoDelete() is not recommended. All you need to do when translating from C++ to Python is to remove the calls to those functions; and it will work like a charm.

It is useful to know how IVuPy takes care of the Open Inventor reference counts when you need to debug IVuPy:

  1. When IVuPy instantianates a Python object derived from SoBase, it sets its Open Inventor reference count to 1 to prevent its deletion by the C++ library under normal conditions (it can still happen if there is a bug in the C++ library).
  2. When the Python refercence count of such an instance goes to 0, IVuPy checks the Open Inventor reference count. If it equals 1, it can be safely deleted. It it is bigger than 1, the Open Inventor reference count is decremented and the instance is marked as owned by C++. The instance can be deleted by the C++ library.
  3. When such an instance is returned to Python, IVuPy checks if the instance is marked as owned by C++. If so, the instance is marked as owned by Python and its Open Inventor reference count is incremented to prevent deletion by the C++ library.

The rules are illustrated by the following example:

#!/usr/bin/env python

from iv import *

def make():
    print 'Make a separator.'
    s = SoSeparator()
    print 'Make a cone.'
    c = SoCone()
    print 'Add the cone to the separator.'
    s.addChild(c)
    print 's.getRefCount() =', s.getRefCount()
    print 'c.getRefCount() =', c.getRefCount()
    print 'Return the separator.'
    print 'The Python reference to the cone goes out of scope.'
    return s

if __name__ == '__main__':
    s = make()
    print 's.getRefCount() =', s.getRefCount()
    print 'Get the cone from the separator'
    c = s.getChild(0)
    print 'c.getRefCount() =', c.getRefCount()
    print 'The Python reference to the separator and cone go out of scope.'

# Local Variables: ***
# mode: python ***
# End: ***

If IVuPy has been built with the C++ preprocessor symbol TRACE_IVUPY defined, the output looks like:

000: [packer@titan ivupy]$ python refcounting.py
001: Make a separator.
002: iv: sipSoSeparator(): ref() to 1
003: Make a cone.
004: iv: sipSoCone(): ref() to 1
005: Add the cone to the separator.
006: s.getRefCount() = 1
007: c.getRefCount() = 2
008: Return the separator.
009: The Python reference to the cone goes out of scope.
010: iv: dealloc_SoCone(0xb4cbce0c): unref() to 1, ownership to C++
011: s.getRefCount() = 1
012: Get the cone from the separator
013: iv: convertFrom_SoNode to 0xb4cbce0c: ref() to 2, ownership to Python
014: c.getRefCount() = 2
015: The Python reference to the separator and cone go out of scope.
016: iv: dealloc_SoCone(0xb4cbce0c): unref() to 1, ownership to C++
017: iv: dealloc_SoSeparator(0xb4d06e9c): unrefNoDelete() to 0, SIP deletes
018: [packer@titan ivupy]$