PyObject_HasAttrString segfaults when attribute doesn't exist, doesn't when exists
我在使用python c API时遇到了一个非常奇怪的问题。我有一个全局范围的结构"dcon",其中包含pyobject"device"成员。
1 2 3 4 5 6 7 8 9 10 11 | static status_t get_mac_addr(uint8_t const ** addr, size_t * const size){ static uint8_t device_mac_addr[6] = {0}; *addr = device_mac_addr; *size = sizeof device_mac_addr; if(PyObject_HasAttrString(dcon.device,"mac_address") == 1){ ... } return 0; } |
似乎只要存在'mac_address'属性,代码就可以正常执行。奇怪的是,当'mac_address'不是定义的属性时,我遇到一个segault(
1 2 3 4 5 6 7 | (gdb) bt #0 0x00000000004ca607 in PyErr_Restore () #1 0x00000000004aa29f in PyErr_Format () #2 0x000000000049e6ee in _PyObject_GenericGetAttrWithDict () #3 0x0000000000486387 in PyObject_GetAttrString () #4 0x00000000004ea7d7 in PyObject_HasAttrString () #5 0x00007ffff4f2056d in get_mac_addr (size=0x7ffff4f1cd28, addr=<optimized out>) at config.c:165 |
我对python c api有点入门。我最初的想法是我做了一些错误的参考计数,但我似乎不能把我的头围绕它。
在多线程应用程序中,任何调用python c api函数的线程都必须首先确保该线程持有全局解释器锁。对于从python代码调用的函数,这不是问题,因为锁已经被持有。
对于从解释器外部调用的代码(回调等),情况并非如此。
在这种情况下,必须先获取gil,然后才能安全地调用python api函数。
1 2 3 4 5 6 | PyGILState_STATE gstate; gstate = PyGILState_Ensure(); ... PyGILState_Release(gstate); |
这不仅确保了gil被保存,而且还为在python之外创建的线程创建了python线程状态(使用直接操作系统调用,而不是
调用python API时可能发生的任何python异常都必须在释放gil之前处理。
注意,如果另一个线程当前持有锁(例如,因为它正在执行python代码),那么在另一个线程释放锁之前,确保调用将被阻塞。
即使已经获取了锁,也可以使用这些调用,前提是确保()的每个调用都有一个与release()匹配的调用。