13 Mar 2005 titus   » (Journeyer)

Safari --> FireFox and back again

I tried out Firefox on my iBook, because Safari was spinning the little wheel too much. Firefox isn't much better, and has a number of focus problems. Plus it doesn't look nearly as pretty. YMMV.

I'll try out Camino when it hits 0.9...

Fun with Python segfaults

Can anyone spot what's wrong with this C++/Python wrapper code?

PyObject * ret;

Py_BEGIN_ALLOW_THREADS

try { long val = heavy_computation_stuff(); ret = PyInteger_FromLong(val); } catch (program_exception & e) { PyErr_SetString(PyExc_Exception, "whoops, I got broke"); }

Py_END_ALLOW_THREADS

return ret;

(It segfaults.)

I'll give you a hint: it has to do with the global interpreter lock.

Oh, and there are actually two bugs in the code, but only one

actually causes a crash.

...

Well, I'm sure you're on tenterhooks now, so I'll give you the answer to the guaranteed segfault: you need to wrap PyErr_SetString in Py_BLOCK_THREADS/Py_UNBLOCK_THREADS.

...

OK, well, the other bug is the same thing, it just doesn't cause problems in this particularl instance: PyInteger_FromLong also needs to be wrapped in Py_BLOCK_THREADS/Py_UNBLOCK_THREADS.

I forgot the cardinal rule of the GIL: any time you access Python code, you need to turn threads off.

ARRRGGGH, I swear this took me the better part of a day to figure out.

But now I'm stuck. Without try/finally in C++ I can't guarantee cleanup if I do an ALLOW_THREADS in the try block. And it'd be severely ugly (not to mention moderately error-prone) to set a flag when an exception is raised, e.g.

BEGIN_ALLOW_THREADS
try {
   ...
   END_ALLOW_THREADS
} catch (...) {
   exception_raised = true;
}
if (exception_raised) { END_ALLOW_THREADS; }

(Yeah, I'd need to redefine the macros to make this work anyway.)

Update

Chris Frey and Peter Hart pointed out that you can get the same functionality as try/finally by using classes. So my solution now looks like this:

try {
   { py_thread_saver save;
   val = long_computation();
   }
   ret = PyInt_FromLong(val);
} except (...) {
   ...
}

Since the py_thread_save object is an automatic variable, it gets destroyed at the end of the code block.

The py_thread_saver class is pretty simple:


// a class to automatically handle saving of thread state. class py_thread_saver { protected: PyThreadState * _tstate; public: py_thread_saver() { _tstate = PyEval_SaveThread(); } ~py_thread_saver() { PyEval_RestoreThread(_tstate); } };

there are a bunch of good variations on this that can fit more complicated scenarios, but this solves my problems perfectly. Thanks, guys!

--titus

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!