On Feb 3, 2009, at 11:37 PM, Victor Lin wrote:

It does not work. But however, thanks your help. I have tired so many
methods to do. But it
crash...crash..deadlock...deadlock..crash...crash... I have no any
tried success. I am going crazy. Could someone help me, thanks.


Hi Victor,
I have some code that works, although I'm not terribly confident of it. The Python documentation in this area is as clear as mud. I'd be interested to see if my code works for you as well.

This is part of my function that the Python code calls to set up the callback:

if (!PyEval_ThreadsInitialized()) {
   DPRINTF("calling PyEval_InitThreads()\n");
   PyEval_InitThreads();
   // PyEval_InitThreads() acquires the GIL on my behalf but
   // I don't want it at the moment.
   PyEval_ReleaseLock();
}

This sets up the GIL if necessary (i.e. if this is a single-threaded program) and is a no-op otherwise (i.e. if the app has already created a Python thread).

Then I have this function to perform the callback. It is invoked in a new C thread. Comments are inline.

void process_notification(union sigval notification_data) {
/* Invoked by the system in a new thread as notification of a message
       arriving in the queue. */
    PyObject *arglist;
    PyObject *result;
    PyGILState_STATE gstate;
    PyThreadState *main_thread;
    PyThreadState *callback_thread;
    MessageQueue *self = notification_data.sival_ptr;

    DPRINTF("C thread %ld invoked\n", pthread_self());

    // PyGILState_Ensure() implicitly acquires the GIL so I don't need
    // to call PyEval_AcquireLock().
    DPRINTF("Calling PyGILState_Ensure()\n");
    gstate = PyGILState_Ensure();

    // Get the current thread state so that I have an interpreter to
    // which to point.
    DPRINTF("Calling PyThreadState_Get()\n");
    main_thread = PyThreadState_Get();

    // Create a new Python thread for the callback.
    DPRINTF("Calling PyThreadState_New()\n");
    callback_thread = PyThreadState_New(main_thread->interp);

    // Make the callback thread current.
    DPRINTF("Calling PyThreadState_Swap()\n");
    PyThreadState_Swap(callback_thread);

    // Perform the callback.
    arglist = Py_BuildValue("(O)", self->notification_function_param);
    result = PyEval_CallObject(self->notification_function, arglist);
    Py_DECREF(arglist);

    DPRINTF("Done calling\n");

    // Clean up my internal pointers
    Py_XDECREF(self->notification_function);
    Py_XDECREF(self->notification_function_param);
    self->notification_function = NULL;
    self->notification_function_param = NULL;

    // Now unwind the Python thread/GIL stuff above
    DPRINTF("Calling PyThreadState_Swap()\n");
    PyThreadState_Swap(main_thread);

    DPRINTF("Calling PyThreadState_Clear()\n");
    PyThreadState_Clear(callback_thread);

    DPRINTF("Calling PyThreadState_Delete()\n");
    PyThreadState_Delete(callback_thread);

// PyGILState_Ensure() acquires the lock, but does PyGILState_Release() // release it? The documentation doesn't say, but it seems like it does.
    DPRINTF("Calling PyGILState_Release()\n");
    PyGILState_Release(gstate);

    DPRINTF("exiting thread\n");
};



This code works (in my limited testing) regardless of whether or not the Python code has created a thread. For the threaded test, I created a background thread that prints "ding!" every second. That thread continued to run even after my callback thread was invoked which I assume means that I released the GIL properly.

As I mentioned before, this is part of my posix_ipc extension. This code (assuming I feel confident enough to release it) will be in the next version that should be out soon, so you will have a full working example with which to experiment.

HTH,
Philip



--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to