Calling python from C with OpenMP

oysteijo at gmail.com oysteijo at gmail.com
Thu May 12 15:28:40 EDT 2016


Hi,

I have a framework written in C and I need to call Python from that framework. I have written the code, and it runs fine, however when I recompile with OpenMP enabled, I get segmentation faults and some times an error message: 

Fatal Python error: GC object already tracked

I'm able to reconstruct the bug with this simple code:

/* main.c */
#include <Python.h>
#include <omp.h>
int main()
{
    wchar_t *program = Py_DecodeLocale( "My_problem", NULL );
    if( !program ){
        fprintf(stderr, "cannot decode python program name\n");
        return -1;
    }

    Py_SetProgramName( program );
    Py_Initialize();

    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyUnicode_FromString("."));

    PyObject *module_filename = PyUnicode_FromString( "multiplier" );
    if(!module_filename){
        printf("Cannot create python module  multiplier.\n");
        return -1;
    }
    PyObject *module = PyImport_Import( module_filename );
    if(!module){
        printf("Cannot create python.\n");
        return -1;
    }

    PyObject *mult_obj = PyObject_CallMethod( module ,"multiplier", "i", 7);
    if(!mult_obj){
        printf("Cannot create python multiplier class instance\n");
        return -1;
    }

    Py_DECREF( module );
    Py_DECREF( module_filename );
    Py_DECREF( path );
    Py_DECREF( sys );
    /* Up to now we have actually done:
     * >>> import multiplier
     * >>> mult_obj = multipier.multiplier(7)
     */

    /* lets try something like:
     * >>> for x in range(10):
     * ...     printf(mult_obj.do_multiply(x))
     */

#pragma omp parallel for 
    for( int i = 0; i < 10; i++ ){
        PyObject *ret = PyObject_CallMethod( mult_obj, "do_multiply", "i", i );
        if( !ret ){
            printf("Cannot call 'do_multiply'\n");
            continue;
        }
        printf("The value calculated in Python was: %3d\n", (int) PyLong_AsLong(ret));
        Py_DECREF(ret);
    }

    Py_DECREF(mult_obj);
    Py_Finalize();

    return 0;
} 

Compile with:
gcc -std=gnu99 -O3 -Wall -Wextra -fopenmp `pkg-config --cflags --libs python3` -lgomp main.c -o main

Then you need the python code:

# multiplier.py
class multiplier(object):
    def __init__(self, factor):
        self.factor = factor
    def do_multiply(self, x):
        return self.factor * x

First question: Does my C code leak memory? Valgrind says it does, but the memory footprint of the executable is stable while looping?

Second and most important question: When I run this code it sometimes segementation faults, and sometimes some threads run normal and some other threads says "Cannot call 'do_multiply'". Sometimes I get the message: Fatal Python error: GC object already tracked. And some times it even runs normally...
I understand there is some kind of race condition here, where python tries to refer to some memory that has been already released. But how can I avoid this? What am I doing wrong? (or, less likely, is this a bug?)

Maybe needless to say, but the code works when compiled w/o OpenMP.

Using Python 3.5.1

Thanks,
-Øystein



More information about the Python-list mailing list