python 在主函数范围外调用时PyObject_CallObject崩溃

42fyovps  于 2023-02-02  发布在  Python
关注(0)|答案(1)|浏览(425)

我正在构建一个简单的模块来 Package 一个C函数,这个模块的main函数(test_wrapper)基本上接收一个python函数并调用它:

#include <Python.h>

static PyObject* test_wrapper(PyObject* self, PyObject* args) {
    PyObject* py_handler;
    int args_ok = PyArg_ParseTuple(args, "O", &py_handler);

    PyObject_CallObject(py_handler, NULL);

    return Py_BuildValue("i", 0);
}

static PyMethodDef TestModuleMethods[] = {
    { "test", test_wrapper, METH_VARARGS, NULL },
    { NULL, NULL, 0, NULL }
};

static struct PyModuleDef TestModule = {
    PyModuleDef_HEAD_INIT,
    "test_module",
    NULL,
    -1,
    TestModuleMethods
};

PyMODINIT_FUNC PyInit_test_module(void) {
    return PyModule_Create(&TestModule);
}

上面的代码运行良好,但问题是,假设我以后需要用另一种方式调用传递过来的python函数(py_handler),比如用信号处理程序,现在它需要一个整数作为参数:

PyObject* py_handler;

void handler(int signo) {
    PyObject* handler_args = PyTuple_Pack(1, PyLong_FromLong(signo));
    PyObject_CallObject(py_handler, handler_args); //seg fault
}

static PyObject* test_wrapper(PyObject* self, PyObject* args) {
    int args_ok = PyArg_ParseTuple(args, "O", &py_handler);
    //Py_INCREF(py_handler); //adding this didn't work

    //calls sigaction to set handler function

    return Py_BuildValue("i", 0);
}

通过这样做,PyObject_CallObject在被handler调用时崩溃(seg错误)。
我能错过什么呢?
如果相关,我将使用setup.py构建.so

gmol1639

gmol16391#

收购并释放GIL就足以解决这个问题:

void handler(int signo) {
    PyGILState_STATE state = PyGILState_Ensure();
    PyObject* handler_args = PyTuple_Pack(1, PyLong_FromLong(signo));
    PyObject_CallObject(py_handler, handler_args);
    PyGILState_Release(state);
}

将代码 Package 在PyGILState_STATE对象中,并在执行后释放它,可以确保一次只有一个线程在执行Python代码。通常,在调用任何Python API函数之前获取GIL,然后释放它是一个很好的做法。

相关问题