0

I have a simple c++ class that I've built a wrapper for to interface it with python 2.7. Now, I would like to be able to call a method of that class, ExampleClass::simulate(double sim_time) through python and pause the method/thread by calling another method, ExampleClass::pause(). A very simplistic example would look something like this:

#include<iostream>
#include<unistd.h>

class ExampleClass {
  public:
    void simulate(double sim_time)
    {
      pause_ = false;
      int count = 0;
      while (count < 10) {
        std::cout << "simulating.\n";
        sleep(1);
        while(pause_) {
          std::cout << "paused.\n";
          sleep(1);
        }
        count += 1;
      }
    }
    void pause()
    {
      pause_ = true;
    }
    void resume()
    { 
      pause_ = false;
    }
  private:
    bool pause_;
};

With the corresponding python wrapper:

    void simulate_wrapper(ExampleClass* ec, double t_sim)
    {
      PyGILState_STATE gstate = PyGILState_Ensure();
      ec->simulate(t_sim);
      PyGILState_Release(gstate);
    } 

    BOOST_PYTHON_MODULE(ExampleExt)
    {
      PyEval_InitThreads();
      boost::python::class_<ExampleClass>("ExampleClass")
      .def("pause",&ExampleClass::pause)
      .def("resume",&ExampleClass::resume)
      .def("simulate", &simulate_wrapper);
    }

Then in python, I have:

    import threading
    import time
    import ExampleExt
    ec = ExampleExt.ExampleClass()
    t_sim = 2 

    def simulate(ec, t_sim):
            print ('starting simulation.')
            t = time.clock()
            ec.simulate(t_sim)
            dt = time.clock() - t
            print ('simulation took',dt, 'seconds')

    threading1 = threading.Thread(target=simulate, args=(ec,t_sim))
    threading1.daemon = True
    threading1.start()

But when I create a ExampleClass object in ipython and run the simulate function, I can't run any other commands. I was reading about GIL and apparently it blocks all python I/O events so I guess it's really not what I should be doing. Especially because I want to modify the member pause_ whilst the simulate method is running. Any thoughts on how to make this work?

Thanks

Edit: I had a go at boost::thread to see whether that would solve my problem, with some interesting results. This is how I changed my wrapper:

  #include<example_class.hh>
  #include<iostream>
  #include<boost/python.hpp>
  #include<boost/thread/thread.hpp>
  #include<boost/bind.hpp>

  void simulate_wrapper(ExampleClass* ec, double t_sim)
  {
      boost::thread t1(boost::bind(&ExampleClass::simulate,ec, t_sim));
  } 

And my .py script just looks like this:

import ExampleExt
ec = ExampleExt.ExampleClass()
t_sim = 2
ec.simulate(t_sim)

Everything else is the same as before. Now when I run the .py script, I can use the pause and resume methods without problems but when the simulate method finishes, I get a SIGSEGV. What could be causing it?

Thanks for any insights!

borizzzzz
  • 620
  • 1
  • 6
  • 17
  • In the second case, the `ExampleClass` instance probably goes out of scope (gets destroyed and deallocated) before the thread finishes (Python is in no way aware that you fired off another thread that shares that instance). – Dan Mašek Sep 17 '18 at 16:52
  • Thanks @DanMašek, I'll have a look at that. But somehow it seems improbable as I create the instance in python and do nothing to delete it after... – borizzzzz Sep 18 '18 at 12:47
  • Do you run that code in the interactive interpreter, or as a script? In the second case... think about what happens after the last statement is executed. (Add some simple debugging output to the constructor and desctructor of your class, so you see what happens in more detail) – Dan Mašek Sep 18 '18 at 12:54
  • 1
    I try it interactively because as a script it wouldn't work. You would just exit right away without waiting for the thread to finish. In the interactive case the destructor never gets called, the segmentation fault happens before. – borizzzzz Sep 18 '18 at 13:39
  • Right, that's what I had in mind. I was mislead by the "when I run the .py script" bit ;) Interesting... – Dan Mašek Sep 18 '18 at 13:45
  • In that case the problem is most likely due to `boost::thread` destructor being called while the thread is still joinable. Try to `t1.detach()` right after creation, and see if it stops the segfaults. Not that it's a solution but it should bring you a step closer to it. – Dan Mašek Sep 18 '18 at 14:01

0 Answers0