swig/Lib/python/pyclasses.swg
Thomas Maslach de6b433cb1 Fix Python crash when using -threads iterating containers
Also fixes li_std_vector_enum testcase when run with -threads.

Patch supplied on swig-devel mailing list on 12 Sep with details...

==============================================
I just wanted to mention that I found a crash issue in bug..

I am using SWIG 2.0.11 with python and have –threads enabled.  I have a C++ std::vector that I instantiate in SWIG with %template.  I also have a method in a class that returns this vector.  I also include std_vector.i, btw..

When I iterate like so:

children = Action.getActionList()
for child in children:
  pass

Everything is fine..

When I iterate like this:

for child in Action.getActionList()
  pass

Product crashes.

The problem is the following.  This code gets called first:

SWIGINTERN PyObject *_wrap_delete_SwigPyIterator(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  swig::SwigPyIterator *arg1 = (swig::SwigPyIterator *) 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  PyObject * obj0 = 0 ;

  if(!PyArg_UnpackTuple(args,(char *)"delete_SwigPyIterator",1,1,&obj0)) SWIG_fail;
  res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_swig__SwigPyIterator, SWIG_POINTER_DISOWN |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_SwigPyIterator" "', argument " "1"" of type '" "swig::SwigPyIterator *""'");
  }
  arg1 = reinterpret_cast< swig::SwigPyIterator * >(argp1);
  {
    SWIG_PYTHON_THREAD_BEGIN_ALLOW;
    delete arg1;
    SWIG_PYTHON_THREAD_END_ALLOW;
  }
  resultobj = SWIG_Py_Void();
  return resultobj;
fail:
  return NULL;
}

Note the SWIG_PYTHON_THREAD_BEGIN_ALLOW/END_ALLOW. In between those two statements, we delete arg1.  That in turn will eventually end up in this code:

namespace swig {
  class SwigPtr_PyObject {
  protected:
    PyObject *_obj;

  public:
    … snip! …
    ~SwigPtr_PyObject()
    {
      Py_XDECREF(_obj);
    }

Uh-oh!  We call Py_XDECREF when we aren’t supposed to because we are in a SWIG_PYTHON_THREAD_BEGIN_ALLOW/END_ALLOW section!

This takes care of the issue:

namespace swig {
  class SwigPtr_PyObject {
  protected:
    PyObject *_obj;

  public:
    … snip! …
    ~SwigPtr_PyObject()
    {
      SWIG_PYTHON_THREAD_BEGIN_BLOCK;
      Py_XDECREF(_obj);
      SWIG_PYTHON_THREAD_END_BLOCK;
    }

There are several other methods in this class that use the Python API, but don’t have the BEGIN/END block defined.  I’m not sure if they are required for all of them, but I believe they are..

I have attached a modified pyclasses.swg with what I believe are the correct changes.  This code is from 2.0.11, but as far as I can tell, it’s the same as what is in 3.0.2…

Apologies for not doing more here (making/running tests, getting it in the code repository, etc..), but I’m under some pressure to get some unrelated things done…
2014-09-23 22:33:25 +01:00

157 lines
3.4 KiB
Text

#ifdef __cplusplus
/*
SwigPtr_PyObject is used as a replacement of PyObject *, where
the INCREF/DECREF are applied as needed.
You can use SwigPtr_PyObject in a container, such as
std::vector<SwigPtr_PyObject>;
or as a member variable:
struct A {
SwigPtr_PyObject obj;
A(PyObject *o) : _obj(o) {
}
};
or as a input/output value
SwigPtr_PyObject func(SwigPtr_PyObject obj) {
SwigPtr_PyObject out = PyString_FromFormat("hello %s", PyObject_AsString(obj));
Py_DECREF(out);
return out;
}
just remember to pair the object creation with the proper DECREF,
the same as with plain PyObject *ptr, since SwigPtr_PyObject always add
one reference at construction.
SwigPtr_PyObject is 'visible' at the wrapped side, so you can do:
%template(pyvector) std::vector<swig::SwigPtr_PyObject>;
and all the proper typemaps will be used.
*/
namespace swig {
%ignore SwigPtr_PyObject;
struct SwigPtr_PyObject {};
%apply PyObject * {SwigPtr_PyObject};
%apply PyObject * const& {SwigPtr_PyObject const&};
%typemap(typecheck,precedence=SWIG_TYPECHECK_SWIGOBJECT,noblock=1) SwigPtr_PyObject const& "$1 = ($input != 0);";
/* For output */
%typemap(out,noblock=1) SwigPtr_PyObject {
$result = (PyObject *)$1;
Py_INCREF($result);
}
%typemap(out,noblock=1) SwigPtr_PyObject const & {
$result = (PyObject *)*$1;
Py_INCREF($result);
}
}
%{
namespace swig {
class SwigPtr_PyObject {
protected:
PyObject *_obj;
public:
SwigPtr_PyObject() :_obj(0)
{
}
SwigPtr_PyObject(const SwigPtr_PyObject& item) : _obj(item._obj)
{
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
Py_XINCREF(_obj);
SWIG_PYTHON_THREAD_END_BLOCK;
}
SwigPtr_PyObject(PyObject *obj, bool initial_ref = true) :_obj(obj)
{
if (initial_ref) {
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
Py_XINCREF(_obj);
SWIG_PYTHON_THREAD_END_BLOCK;
}
}
SwigPtr_PyObject & operator=(const SwigPtr_PyObject& item)
{
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
Py_XINCREF(item._obj);
Py_XDECREF(_obj);
_obj = item._obj;
SWIG_PYTHON_THREAD_END_BLOCK;
return *this;
}
~SwigPtr_PyObject()
{
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
Py_XDECREF(_obj);
SWIG_PYTHON_THREAD_END_BLOCK;
}
operator PyObject *() const
{
return _obj;
}
PyObject *operator->() const
{
return _obj;
}
};
}
%}
/*
SwigVar_PyObject is used to manage 'in the scope' PyObject * variables,
as in
int func () {
SwigVar_PyObject obj = PyString_FromString("hello");
}
ie, 'obj' is created and destructed in the same scope from
a python object that carries at least one reference value.
SwigVar_PyObject just take care of applying the proper Py_DECREF.
Hence, this class is purely internal and not visible at the wrapped side.
*/
namespace swig {
%ignore SwigVar_PyObject;
struct SwigVar_PyObject {};
%apply PyObject * {SwigVar_PyObject};
%apply PyObject * const& {SwigVar_PyObject const&};
}
%{
namespace swig {
struct SwigVar_PyObject : SwigPtr_PyObject {
SwigVar_PyObject(PyObject* obj = 0) : SwigPtr_PyObject(obj, false) { }
SwigVar_PyObject & operator = (PyObject* obj)
{
Py_XDECREF(_obj);
_obj = obj;
return *this;
}
};
}
%}
#endif