[Pyrex] Conditional compilation

David M. Cooke cookedm at physics.mcmaster.ca
Thu Apr 7 09:53:24 CEST 2005


khinsen at cea.fr writes:

> On 05.04.2005, at 22:21, David M. Cooke wrote:
>
>> If you're using Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, then the
>> stuff between them should be pure C code: how about making that a
>> function and throwing that into a header file, and including it?
>>
> That's what I have been doing (with macros rather than functions), but
> it's a bit of a pain with a dozen or so cases in a single module, and
> it doesn't do any good to readability.

I think for this particular case it would be better to add
functionality to Pyrex. The problem is you need to *guarantee* that
you're not jumping out of the *_ALLOW_THREADS block, and that you're
not using any of the Python API inside it.

But I came up with another solution. In a header file,

#define begin_allow_threads() Py_BEGIN_ALLOW_THREADS
#define end_allow_threads() Py_END_ALLOW_THREADS

Your Pyrex file:

cdef extern from "someheader.h":
    void begin_allow_threads()
    void end_allow_threads()

def some_function():
    cdef int result, a, b
    a = b = 1
    begin_allow_threads()
    result = a + b
    end_allow_threads()

The relevant portions of the generated code for me looks like

static PyObject *__pyx_f_1x_some_function(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_1x_some_function(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  int __pyx_v_result;
  int __pyx_v_a;
  int __pyx_v_b;
  PyObject *__pyx_r;
  static char *__pyx_argnames[] = {0};
  if (!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames)) return 0;

  /* "/home/cookedm/temp/x.pyx":11 */
  __pyx_v_a = 1;
  __pyx_v_b = 1;

  /* "/home/cookedm/temp/x.pyx":12 */
  begin_allow_threads();

  /* "/home/cookedm/temp/x.pyx":13 */
  __pyx_v_result = (__pyx_v_a + __pyx_v_b);

  /* "/home/cookedm/temp/x.pyx":14 */
  end_allow_threads();

  __pyx_r = Py_None; Py_INCREF(__pyx_r);
  goto __pyx_L0;
  __pyx_L1:;
  __Pyx_AddTraceback("x.some_function");
  __pyx_r = 0;
  __pyx_L0:;
  return __pyx_r;
}

As required, no Python calls occur between the begin_allow_threads /
end_allow_threads calls (which should just expand to
Py_*_ALLOW_THREADS). As long as you're careful not to use C API calls
in between, you're ok. You could even write a script to scan the
generated result for the pattern Py.* in between *_allow_threads()
pairs to warn you :-)

Works with 0.9.3; whether you'll trust it to work for future versions
is something you'll have to decide ;-)

-- 
|>|\/|<
/--------------------------------------------------------------------------\
|David M. Cooke                      http://arbutus.physics.mcmaster.ca/dmc/
|cookedm at physics.mcmaster.ca



More information about the Pyrex mailing list