[C++-sig] Segfaults in object deallocation

Alex Leach beamesleach at gmail.com
Sun Mar 24 20:22:02 CET 2013


Thanks for the reply, and apologies for the delay in mine; I've been  
struggling with classes having protected destructors. Sorted now though..

On Sat, 23 Mar 2013 03:02:58 -0000, Niall Douglas  
<s_sourceforge at nedprod.com> wrote:

>
> Separate C++ objects from their python wrappers. Have C++ objects
> held by shared_ptr.
>
> Have a custom deleter on shared_ptr invoke a virtual
> int_PythonIsAboutToDelete() function before it actually calls delete.
> You see as soon as destruction begins, all virtual functions cease to
> work, so you need a predestructor stage.

This seems to be on the right track. I had some success when calling a  
Python-exposed __del__ method, with std::auto_ptr<Environment> as  
argument. I had found - with lots of printf calls - that the C++  
destructor was being called twice. Each time, it called  
pthread_mutex_destroy, and being one too many times, that caused a  
seg-fault. Seemed like an ownership problem, and calling 'release' on  
std::auto_ptr<Environment>, in the exposed "__del__" method, got around  
that seg-fault, running without error.

I was in the middle of replying with a success story, but on further  
testing, it seemed that this solution only worked when the library was  
linked against libpython3 / libboost_python3. When linking against the  
version 2.7 counterparts, seg-faults crept back in... I couldn't  
understand why...

With that partial success, I thought that I should define a corresponding  
__new__ method, to allocate the memory for the object deleted by __del__.

Although that seems okay to me in principal, I'm completely failing to  
write a successful implementation...

Are there any boost python helpers for this? Again, any help greatly  
appreciated!


// wrapper class

namespace mylib {
     class IEnvironment
         : public notmylib::Environment
     {
         IEnvironment(const boost::python::list& envp);
         ~IEnvironment(void) {}

         // method implementing __new__ functionality?
         static boost::shared_ptr<IEnvironment>  Create(
             boost::python::object type,
             const boost::python::list *envp );
     }

     // custom deleter
     void DelEnvironment(boost::shared_ptr<IEnvironment> env);

     // pointer to start of environment array
     char** m_envp;

     // thread and gil state vars
     PyThreadState *       _save;
     PyGILState_STATE  gil_state;
}


// First function to be called in initialisation chain.
const char* const* mylib::IEnvironment::InitEnvironment(const  
boost::python::list& envp)
{
     int envc = boost::python::len(envp);
     pylib::m_envp = new char*[envc+1];
     stl_input_iterator<char*>   begin(envp)   end;
     int i=0;
     while ( begin != end )
     {
         pylib::m_envp[i++] = (*begin);
         begin++;
     }
     pylib::m_envp[i] = '\0';

     pylib::gil_state = PyGILState_Ensure();
     pylib::_save = PyEval_SaveThread();

     return &pylib::m_envp[0];
}

// Defines initialiser chain. The function body here runs last.
mylib::IEnvironment::IEnvironment(const boost::python::list& envp)
     : Environment(InitIEnvironment(boost::ref(envp)))
{
     PyEval_RestoreThread(pylib::_save);
     PyGILState_Release(pylib::gil_state);
}



// attempt at writing a __new__ method
boost::shared_ptr<mylib::Environment>    mylib::Environment::Create(
     boost::python::object cls, const boost::python::list& envp )
{
     PyTypeObject* cls_type = (PyTypeObject*)cls.ptr();
     PyObject* cls_inst  = PyType_GenericNew( cls_type, envp.ptr(),  
Py_None);
     if (PyType_Ready(cls_type) != 0)
     {
         throw error_already_set();
     }
     // lots of trial and error here. Always in error, so far...
     return boost::make_shared<pylib::IEnvironment>(
             boost::python::extract<pylib::IEnvironment>(
                 boost::python::object(boost::python::ptr(cls_inst))));
}



BOOST_PYTHON_MODULE(foo)
{
     boost::python::class_<mylib::Environment, boost::noncopyable,
                           boost::shared_ptr<mylib::IEnvironment>
         ("Environment", init<const boost::python::list&>() )

         .def("__new__", &mylib::IEnvironment::Create )
         .staticmethod("__new__")  //< necessary?

         .def("__del__", &pylib::DelEnvironment )
         ;
}


>>
> Do your pre destruction cleanup like destroying mutexs in that
> virtual function. Or indeed anything which can throw exceptions,
> because destructors are (soon to be) noexcept.
>
> Hope that helps,
> Niall


Yep, thanks!

Cheers,
Alex


-- 
Using Opera's mail client: http://www.opera.com/mail/


More information about the Cplusplus-sig mailing list